1 Revision History

This version

Current version: 1
Date report generated: 15 December, 2020
Report prepared for: Name
Purpose of report:

  • Exercise to analyze RNA-Seq data

Previous revisions

N/A


2 Code setup, Data Loading, and DESeq2 Code

This report is meant to help explore DESeq2 results and was generated using RMarkdown. This section contains the code for setting up the rest of the report.

2.1 Load libraries

## Define paths
paths <- list()
paths$root <- params$projectdir
paths$data <- paste0(paths$root, "/data")
paths$raw <- paste0(paths$data, "/raw")
paths$output <- paste0(paths$data, "/output")
paths$processed <- paste0(paths$data, "/processed")
paths$metadata <- paste0(paths$root, "/metadata")
paths$reports <- paste0(paths$root, "/reports")
if(is.na(params$group_facet)) {
  paths$DEG_output <- paste0(paths$root, "/DEG_output")
} else {
  paths$DEG_output <- paste0(paths$root, "/DEG_output/group_", params$group_filter)
}
paths$RData <- paste0(paths$DEG_output, "/RData")

## knitrBoostrap and device chunk options
# Set so that long lines in R will be wrapped:
knitr::opts_chunk$set(bootstrap.show.code = FALSE,
                      bootstrap.panel = TRUE,
                      cache = FALSE)
#knitr::tidy.opts=list(width.cutoff=60), tidy=TRUE, crop = NULL # Use for PDFs
knitr::opts_knit$set(root.dir = paths$root)

# Set this variable to FALSE if you only want to run the plotting functions by reloading the last RData file.
flag=params$flag # To evaluate analysis chunks
if (flag==FALSE) { load(paste0(params$projectdir,"Partial_analysis.RData")) }
# Set this variable to TRUE if you would like to embed the files directly into the HTML for portability. This slows down page responsiveness drastically, since the files are generally quite large.
embedFiles=FALSE
# Se this variable to be TRUE if you want to have separate plots of top genes as defined in the R-ODAF template
R_ODAF_plots=FALSE
pathway_analysis <- file.path(paths$root,"Rmd/Pathway_analysis.Rmd")
message("If this text is visible by default, this report was produced to test plotting functions and should be used exclusively for testing and development.")
#### Record start time
startTime <- Sys.time()

#### Load Libraries
# General purpose
# library('conflicted')
library('knitr')
library('kableExtra')
library('tidyverse')
library('magrittr')
library('ggplot2')
library('DT')
library('data.table')
library('pheatmap')
library('lattice')
library('tidytext')
library('openxlsx')
library('RColorBrewer')
library('viridis')
library('sessioninfo')
library('plotly')
#library('RMariaDB')

# Bioinformatics-specific
library('DESeq2')
library('edgeR')
library('enrichplot')
library('rWikiPathways')
library('BiocParallel')
library('clusterProfiler')
library('biomaRt')
library('AnnotationHub')
library('vsn')

# Load genome, depends on species
# To add: Zebrafish, Folsomia candida, others?
species <- params$species
if(species=="human"){
  # Human:
  library('org.Hs.eg.db')
  orgdb <- "org.Hs.eg.db"
  species_sci <- "Homo sapiens"
  ensembl_species <- "hsapiens_gene_ensembl"
  species_gene_symbol <- "external_gene_name"
  kegg_organism <- "hsa"
  temposeq_manifest <- "191113_Human_S1500_Surrogate_2.0_Manifest.csv" # or 191004 Human Whole Transcriptome 2.0 Manifest.xlsx
} else {
    if(species=="mouse"){
      # Mouse:
      library('org.Mm.eg.db')
      orgdb <- "org.Mm.eg.db"
      species_sci <- "Mus musculus"
      ensembl_species <- "mmusculus_gene_ensembl"
      species_gene_symbol <- "mgi_symbol"
      kegg_organism <- "mmu"
      temposeq_manifest <- "181130 Mouse S1500+ Surrogate 1.2 Manifest.xlsx" # or 190603 Mouse Whole Transcriptome 1.1 Manifest.xlsx
    }
  else {
    if(species=="rat"){
      # Rat: 
      library('org.Rn.eg.db')
      orgdb <- "org.Rn.eg.db"
      species_sci <- "Rattus norvegicus"
      ensembl_species <- "rnorvegicus_gene_ensembl"
      species_gene_symbol <- "mgi_symbol"
      kegg_organism <- "rno"
      temposeq_manifest <- "190809 Rat Whole Transcriptome 1.0 Manifest.xlsx"
    }
  else {
    if(species=="hamster"){
      # Golden hamster:
      OrgDb.Ma <- query(AnnotationHub(), c("OrgDb","Mesocricetus auratus"))[[1]]
      orgdb <- "OrgDb.Ma"
      species_sci <- "Mesocricetus auratus"
      ensembl_species <- "mauratus_gene_ensembl"
      species_gene_symbol <- "external_gene_name"
    }
  else {
    message("No species picked!")
  }
  }
  }
}

# Options
options(java.parameters = "-Xmx10000m")
theme_set(theme_bw())

The code above (not shown by default) loads the relevant R packages required for analysis.

2.2 Specify parameters

###################################################################################
###################################################################################
# PARAMETERS TO SET MANUALLY                                     
#
# Set file locations
if(!dir.exists(paths$DEG_output)){
  dir.create(paths$DEG_output, recursive = TRUE)
  dir.create(paths$RData, recursive = TRUE)
  }

# FILES TO LOAD
# A. Tab delimited file with merged RSEM.genes.results files:
SampleDataFile <- file.path(paths$processed,"genes.data.tsv")
# B. Tab delimited sample information file with at least 2 columns:
  # 1. sample names identical to the column names of sampleData
  # 2. compound/group/whatever (needs to identify to which experimental group the sample belongs)
SampleKeyFile <- file.path(paths$metadata,"metadata.txt")
# C. Tab delimited file of comparisons (contrasts) to test 
# Group of interest in the left column, control for comparison in the right column
ContrastsFile <- file.path(paths$metadata,"contrasts.txt")

# Specify which groups need to be compared 
contrasts <- read.delim(ContrastsFile, stringsAsFactors=FALSE, sep="\t", header=FALSE,  quote="\"")
short_contrast_names <- paste(contrasts$V1,"v.",contrasts$V2) # Customize these for your experiment... Must be short enough to fit as Excel tab titles.
DESIGN <- params$design # Column name which defines the groups to be compared
intgroup <- params$intgroup # "Interesting groups" - an experimental group and/or covariates

# Various plotting and display options
nBestFeatures <- 20 # The number of best features to make plots of their counts
nBest <- 100 # Number of features to include in table and limiting PCA/clustering analysis
nHeatmap <- 50 # Number of most variable genes for heatmap
nHeatmapDEGs <- 50 # Number of DEGs for heatmap

# Set analysis ID. This ID will be used as prefix for the output files
# Normally, as follows: year - project_name - group_filter
if(is.na(params$group_filter)){
  analysisID <- paste(format(Sys.time(), '%Y'), params$project_name, sep="_")
} else {
  analysisID <- paste(format(Sys.time(), '%Y'), params$project_name, params$group_filter, sep="_")
}
# Specify used platform/technology for data generation:
Platform <- params$platform # Should be one of "RNA-Seq" or "TempO-Seq"
NORM_TYPE <- paste0(analysisID, "_DESeq2_", Platform)
if(Platform=="TempO-Seq"){sampledata_sep <- ","} else {sampledata_sep <- "\t"}
# Misc parameters
digits = 2 # For rounding numbers

The code above (not shown by default) specifies user preferences and data locations.

2.3 Load data

# Load input files
sampleData <- read.delim(SampleDataFile,
                         sep=sampledata_sep,
                         stringsAsFactors=FALSE,
                         header=TRUE, 
                         quote="\"",
                         row.names=1,
                         check.names=FALSE)

DESeqDesign <- read.delim(SampleKeyFile,
                          stringsAsFactors=FALSE,
                          sep="\t",
                          header=TRUE,
                          quote="\"",
                          row.names=1) # Pick column that is used in ID; might be more appropriate to change this!
DESeqDesign$original_names <- rownames(DESeqDesign)
DESeqDesignAsRead <- DESeqDesign

# Parameter-specified exclusions
# Conditionally exclude contrasts and metadata that should be filtered
# 1. Filter by group filter - useful for selecting one treatment at a time. params$group_filter
# 2. Filter manually - useful for removing irrelevant control groups (e.g. reference RNA). params$exclude_groups
# Note that these should only be run if the respective variables are set.
if(!is.na(params$group_facet)) {
  contrasts_to_filter <- DESeqDesign %>%
  dplyr::filter(!!sym(params$group_facet)==params$group_filter) %>%
  pull(params$design) %>% 
  unique()
  contrasts <- contrasts %>% dplyr::filter(V1 %in% contrasts_to_filter)
  DESeqDesign <- DESeqDesign %>% dplyr::filter(!!sym(params$design) %in% (unlist(contrasts) %>% unique()) )
}
if(any(!is.na(params$exclude_samples))) {
  DESeqDesign <- DESeqDesign %>% dplyr::filter(!original_names %in% params$exclude_samples)
}
if(any(!is.na(params$exclude_groups))) {
  DESeqDesign <- DESeqDesign %>% dplyr::filter(!(!!sym(params$design)) %in% params$exclude_groups)
  contrasts_to_filter <- DESeqDesign %>% 
  dplyr::filter(!(!!sym(params$design)) %in% params$exclude_groups) %>%
  pull(params$design) %>% 
  unique()
  contrasts <- contrasts %>% dplyr::filter(V1 %in% contrasts_to_filter)
}
if(!is.na(params$include_only_column) & !is.na(params$include_only_group)) {
  DESeqDesign <- DESeqDesign %>% dplyr::filter((!!sym(params$include_only_column)) %in% params$include_only_group)
  limit_contrasts <- DESeqDesign %>% pull(!!sym(params$design)) %>% unique() %>% as.character()
  contrasts <- contrasts %>% dplyr::filter(V1 %in% limit_contrasts)
}

# Create directories
plotdir <- paste(paths$DEG_output, "/plots/", sep="")
if(!dir.exists(plotdir)) {dir.create(plotdir, recursive = TRUE)}
barplot.dir <- paste(plotdir, "/barplot_genes/", sep="")
if(!dir.exists(barplot.dir)) {dir.create(barplot.dir, recursive = TRUE)}

#Set parameters according to platform
if (Platform=="RNA-Seq"){
  threshold = 1000000 # Number of aligned reads per sample required
  MinCount <- 1
  alpha <- pAdjValue <- 0.05 # Relaxed from 0.01
  linear_fc_filter = 1.5
  biomart_filter="ensembl_gene_id"
} else if (Platform=="TempO-Seq") {
  threshold = 100000 # Number of aligned reads per sample required
  MinCount<- 0.5
  alpha <- pAdjValue <- 0.05 
  linear_fc_filter = 1.5
  biomart_filter="external_gene_name"
  
  biospyder <- read.delim(file.path("~/shared/dbs/biospyder/", temposeq_manifest), # Assay manifest...
                          stringsAsFactors=FALSE,
                          sep="\t",
                          header=TRUE,
                          quote="\"")
  biospyder_annos <- biospyder %>% dplyr::select(PROBE_NAME, ENSEMBL_GENE_ID, GENE_SYMBOL)
  sampleData$PROBE_NAME <- row.names(sampleData)
  sampleData <- sampleData %>%
    mutate(GENE_SYMBOL = str_replace(PROBE_NAME, "_.*", "")) %>%
    dplyr::group_by(GENE_SYMBOL) %>%
    dplyr::summarise_if(.predicate = function(x) is.numeric(x),
                        .funs = c("sum")) %>%
    ungroup()
  sampleData <- as.data.frame(sampleData)
  row.names(sampleData) <- sampleData$GENE_SYMBOL
  sampleData <- sampleData[,-1]
  # biospyder %>% select(ENSEMBL_GENE_ID) %>% distinct() %>% count()
  # sampleData <- sampleData[row.names(sampleData) %in% biospyder$ENSEMBL_GENE_ID,] # If aligned to hg38...
} else { 
  print("Platform/technology not recognized") 
}

The code above (not shown by default) loads user-provided sample meta data (i.e., information about your experiment, also known as colData, or, column data). This also imports the count matrix (i.e., a table of observed counts in which each sample is a column and genes are rows).

The experimental comparisons of interest to be tested in this report are as follows:

knitr::kable(contrasts,
             row.names = F,
             col.names = c("Group of interest", "Control for comparison"),
             caption="Contrasts requested for this analysis.") %>%
  kable_styling(bootstrap_options = "striped", full_width = F, position = "left") %>%
  scroll_box(height = "480px")
Contrasts requested for this analysis.
Group of interest Control for comparison
male_2 male_0
male_1000 male_0
male_5000 male_0

2.4 Run DESeq2

##########
# DESeq2 #
##########
print(NORM_TYPE) # Name of experiment
## [1] "2020_Flame_retardants_male_DESeq2_RNA-Seq"

# First data clean-up: replace NA & remove samples with total readcount < threshold
initialSampleDataCount <- ncol(sampleData)
sampleData[ is.na(sampleData) ] <- 0 
sampleData <- sampleData[,(colSums(sampleData) > threshold)] # 1 million reads required per sample
filteredSampleDataCount <- ncol(sampleData)
# Sometimes extra cleanup may be needed
# colnames(sampleData) <- gsub(pattern="^0", replacement="", x=colnames(sampleData))

samples_before <- nrow(DESeqDesign)

# Sanity check: each sample (row) in the metadata should have a corresponding column in the count data
metadata_in_sampledata <- all(rownames(DESeqDesign) %in% colnames(sampleData))
# Sanity check: each column in the count data should have a corresponding sample (row) in the metadata
sampledata_in_metadata <- all(colnames(sampleData) %in% rownames(DESeqDesign))
# Find samples that were removed because they weren't in metadata
removed <- colnames(sampleData[which(!colnames(sampleData) %in% rownames(DESeqDesign))])
# Reorder the metadata table to correspond to the order of columns in the count data
DESeqDesign <- DESeqDesign[DESeqDesign$original_names %in% colnames(sampleData),]
DESeqDesign <- na.omit(DESeqDesign)
sampleData <- sampleData[,(rownames(DESeqDesign))]
samples_after <- nrow(DESeqDesign)

head(rownames(DESeqDesign))
## [1] "SRR5890399" "SRR5890400" "SRR5890397" "SRR5890398" "SRR5890403" "SRR5890404"
head(colnames(sampleData)) # Output should match
## [1] "SRR5890399" "SRR5890400" "SRR5890397" "SRR5890398" "SRR5890403" "SRR5890404"

intgroups <- params$intgroup
# Intgroups need to be factors for DESeq2
DESeqDesign[intgroups] <- lapply(DESeqDesign[intgroups], factor)
if(!is.na(params$design)) {
  # If there is a dose column, reorder the experimental group (DESIGN) by dose.
  if(any(grepl(x=colnames(DESeqDesign), pattern="dose", ignore.case = T))){
    doseCol <- grep(x=colnames(DESeqDesign), pattern="dose", ignore.case = T)
    design_factor_reordered <- factor(DESeqDesign[[params$design]],
                                      levels=unique(DESeqDesign[[params$design]][order(DESeqDesign[[doseCol]])]),
                                      ordered=FALSE)
    DESeqDesign[[params$design]] <- design_factor_reordered
  } else {
    DESeqDesign[params$design] <- factor(DESeqDesign[,params$design])
  }
}

if (file.exists(file.path(paths$RData, "dds.RData")) & is.na(params$group_facet) & params$use_cached_RData == TRUE) {
  print(paste("Already found DESeq2 object from previous run; loading from disk."))
  load(file.path(paths$RData,"dds.RData"))
  if (!identical(as.data.frame(round(counts(dds))),
                 round(sampleData),0)) {
    print("Not identical")
    }
  } else {
    if(file.exists(file.path(paths$RData, paste0("dds_", params$group_filter, ".RData"))) & !is.na(params$group_facet) & params$use_cached_RData == TRUE) {
      load(file=file.path(paths$RData, paste0("dds_", params$group_filter, ".RData")))
      } else {
      dds <- DESeqDataSetFromMatrix(countData = round(sampleData),
                                    colData = as.data.frame(DESeqDesign),
                                    design = formula(paste("~", DESIGN)) ) # replace with intgroups if you have a good reason to
      dds <- dds[,rownames(DESeqDesign)]
      dds <- dds[rowSums(counts(dds)) > 1]
      dds <- DESeq(dds, parallel = TRUE, BPPARAM=MulticoreParam(params$cpus))
      if(is.na(params$group_facet)) {
        save(dds, file=file.path(paths$RData, "dds.RData"))
      } else {
        save(dds, file=file.path(paths$RData, paste0("dds_", params$group_filter, ".RData")))
      }
    }
}

# ### REMOVE if done above from scratch
# keep <- grep(colData(dds)$chemical, pattern="cells", invert=T, ignore.case = T)
# dds <- dds[,keep]
# dds <- dds[row.names(dds) %in% biospyder$ENSEMBL_GENE_ID,]

# Another sanity check to make sure the object looks correct
resultsNames(dds)
## [1] "Intercept"                 "group_male_2_vs_male_0"    "group_male_1000_vs_male_0" "group_male_5000_vs_male_0"
head(colData(dds))
## DataFrame with 6 rows and 8 columns
##                   name         sex     dose       files    group original_names sizeFactor replaceable
##            <character> <character> <factor> <character> <factor>    <character>  <numeric>   <logical>
## SRR5890399  male_0_bc1        male        0        bc01   male_0     SRR5890399   1.298834        TRUE
## SRR5890400  male_0_bc2        male        0        bc02   male_0     SRR5890400   0.968512        TRUE
## SRR5890397  male_0_bc3        male        0        bc03   male_0     SRR5890397   0.866203        TRUE
## SRR5890398  male_0_bc4        male        0        bc04   male_0     SRR5890398   2.650522        TRUE
## SRR5890403  male_0_bc5        male        0        bc05   male_0     SRR5890403   0.963630        TRUE
## SRR5890404  male_0_bc6        male        0        bc06   male_0     SRR5890404   1.113516        TRUE
head(assay(dds))
##                    SRR5890399 SRR5890400 SRR5890397 SRR5890398 SRR5890403 SRR5890404 SRR5890401 SRR5890402 SRR5890436 SRR5890367 SRR5890409
## ENSRNOG00000000001          0          1          0          0          0          0          0          0          0          0          1
## ENSRNOG00000000007          0          0          0          0          0          0          0          0          0          0          0
## ENSRNOG00000000012          0          0          0          0          0          0          0          0          0          0          0
## ENSRNOG00000000017          1          0          0          0          0          1          0          0          0          0          0
## ENSRNOG00000000021         24         24         12         41         20         26         21          7         10          9         32
## ENSRNOG00000000024        399        519        743       2484        774        962        804        414        918        806       1601
##                    SRR5890410 SRR5890407 SRR5890408 SRR5890413 SRR5890414 SRR5890411 SRR5890412 SRR5890417 SRR5890418 SRR5890376 SRR5890375
## ENSRNOG00000000001          0          0          0          0          0          0          0          0          0          0          0
## ENSRNOG00000000007          0          0          0          0          0          0          0          0          0          1          0
## ENSRNOG00000000012          0          0          0          0          0          0          0          0          0          0          0
## ENSRNOG00000000017          1          0          0          4          0          2          0          3          0          0          0
## ENSRNOG00000000021          9         15         17         12         16         18         13         27         17         24         17
## ENSRNOG00000000024        794        521        429        617        538        553        596        862        915        711        488
##                    SRR5890374 SRR5890373 SRR5890380 SRR5890379 SRR5890378 SRR5890377 SRR5890369 SRR5890368 SRR5890361 SRR5890364 SRR5890365
## ENSRNOG00000000001          0          0          1          0          0          0          0          0          0          1          0
## ENSRNOG00000000007          0          0          0          0          0          0          0          0          0          0          0
## ENSRNOG00000000012          0          0          0          0          0          0          0          0          0          2          0
## ENSRNOG00000000017          3          0          1          0          0          0          1          0          0          2          0
## ENSRNOG00000000021          8         17         10         17         21         28         16         23         10         25         18
## ENSRNOG00000000024        530        394       1124        779       1069        799        755        630        324        794        842
##                    SRR5890427 SRR5890433 SRR5890435 SRR5890420
## ENSRNOG00000000001          0          0          0          0
## ENSRNOG00000000007          0          1          0          0
## ENSRNOG00000000012          0          0          0          0
## ENSRNOG00000000017          0          0          0          1
## ENSRNOG00000000021          6         10         12         16
## ENSRNOG00000000024        511        385        200       1155
head(rowRanges(dds))
## GRangesList object of length 6:
## $ENSRNOG00000000001
## GRanges object with 0 ranges and 0 metadata columns:
##    seqnames    ranges strand
##       <Rle> <IRanges>  <Rle>
##   -------
##   seqinfo: no sequences
## 
## $ENSRNOG00000000007
## GRanges object with 0 ranges and 0 metadata columns:
##    seqnames    ranges strand
##       <Rle> <IRanges>  <Rle>
##   -------
##   seqinfo: no sequences
## 
## $ENSRNOG00000000012
## GRanges object with 0 ranges and 0 metadata columns:
##    seqnames    ranges strand
##       <Rle> <IRanges>  <Rle>
##   -------
##   seqinfo: no sequences
## 
## ...
## <3 more elements>
str(counts(dds))
##  int [1:18433, 1:37] 0 0 0 1 24 399 22 64 21 1 ...
##  - attr(*, "dimnames")=List of 2
##   ..$ : chr [1:18433] "ENSRNOG00000000001" "ENSRNOG00000000007" "ENSRNOG00000000012" "ENSRNOG00000000017" ...
##   ..$ : chr [1:37] "SRR5890399" "SRR5890400" "SRR5890397" "SRR5890398" ...

# Make regularized log object for later plotting
#rld <- tryCatch(rlog(dds), error = function(e) { rlog(dds, fitType = 'mean') })
# Use vst for hundreds of samples!
rld <- vst(dds) # Should this be blind?
 

The code above (not shown by default) uses DESeq2 1.30.0 to test for deferentially abundant genes within the RNA-Seq data.

Prior to running DESeq2, the data was filtered to remove samples that do not have \(10^{6}\) reads per sample.

The user-provided metadata initially included 40 samples.

The count matrix initially included 80 samples (including any reference material samples). After removing samples with less than \(10^{6}\) reads, 76 samples were left. It is FALSE that all the samples provided in the metadata table were also identified in the count matrix. It is FALSE that all the samples in the count matrix were also identified in the metadata table.

Following the removal of other samples (i.e., reference RNA, etc.), there were 37 samples remaining in the experiment. The samples excluded from analysis are shown in the table in the section titled “Sample data”, along with complete sample metadata for the experiment.

2.5 Sample data (metadata about your experiment)

2.5.1 Samples used in this report

This table shows the final list of samples that were used in the data analysis (as well as the corresponding sample information, e.g., to which experimental group samples belong).

# Conditionally sort by dose if that column name is present
if(any(grepl(x=colnames(DESeqDesign), pattern="dose", ignore.case = T))){
  doseCol <- grep(x=colnames(DESeqDesign), pattern="dose", ignore.case = T)
  knitr::kable(DESeqDesign %>% group_by(!!sym(params$design)) %>% arrange(doseCol, .by_group = T),
             row.names =  F,
             caption="Samples and corresponding experimental conditions used in this report") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive")) %>%
  scroll_box(width = "100%", height = "480px")
} else {
  knitr::kable(DESeqDesign %>% arrange(!!sym(params$design)),
             row.names =  F,
             caption="Samples and corresponding experimental conditions used in this report") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive")) %>%
  scroll_box(width = "100%", height = "480px")
}
Samples and corresponding experimental conditions used in this report
name sex dose files group original_names
male_0_bc1 male 0 bc01 male_0 SRR5890399
male_0_bc2 male 0 bc02 male_0 SRR5890400
male_0_bc3 male 0 bc03 male_0 SRR5890397
male_0_bc4 male 0 bc04 male_0 SRR5890398
male_0_bc5 male 0 bc05 male_0 SRR5890403
male_0_bc6 male 0 bc06 male_0 SRR5890404
male_0_bc7 male 0 bc07 male_0 SRR5890401
male_0_bc8 male 0 bc08 male_0 SRR5890402
male_0_bc9 male 0 bc09 male_0 SRR5890436
male_0_bc10 male 0 bc10 male_0 SRR5890367
male_2_bc11 male 2 bc11 male_2 SRR5890409
male_2_bc12 male 2 bc12 male_2 SRR5890410
male_2_bc13 male 2 bc13 male_2 SRR5890407
male_2_bc14 male 2 bc14 male_2 SRR5890408
male_2_bc15 male 2 bc15 male_2 SRR5890413
male_2_bc16 male 2 bc16 male_2 SRR5890414
male_2_bc17 male 2 bc17 male_2 SRR5890411
male_2_bc18 male 2 bc18 male_2 SRR5890412
male_2_bc19 male 2 bc19 male_2 SRR5890417
male_2_bc20 male 2 bc20 male_2 SRR5890418
male_1000_bc21 male 1000 bc21 male_1000 SRR5890376
male_1000_bc22 male 1000 bc22 male_1000 SRR5890375
male_1000_bc23 male 1000 bc23 male_1000 SRR5890374
male_1000_bc24 male 1000 bc24 male_1000 SRR5890373
male_1000_bc25 male 1000 bc25 male_1000 SRR5890380
male_1000_bc26 male 1000 bc26 male_1000 SRR5890379
male_1000_bc27 male 1000 bc27 male_1000 SRR5890378
male_1000_bc28 male 1000 bc28 male_1000 SRR5890377
male_1000_bc29 male 1000 bc29 male_1000 SRR5890369
male_1000_bc30 male 1000 bc30 male_1000 SRR5890368
male_5000_bc32 male 5000 bc32 male_5000 SRR5890361
male_5000_bc33 male 5000 bc33 male_5000 SRR5890364
male_5000_bc34 male 5000 bc34 male_5000 SRR5890365
male_5000_bc35 male 5000 bc35 male_5000 SRR5890427
male_5000_bc36 male 5000 bc36 male_5000 SRR5890433
male_5000_bc37 male 5000 bc37 male_5000 SRR5890435
male_5000_bc40 male 5000 bc40 male_5000 SRR5890420
knitr::kable(DESeqDesign %>% group_by(!!sym(params$design)) %>% tally(),
             row.names = F,
             col.names = c("Experimental group", "Number of samples in group"),
             caption="Number of samples in each experimental group used in this report") %>%
  kable_styling(bootstrap_options = "striped", full_width = F, position = "left") %>%
  scroll_box(width = "100%", height = "480px")
Number of samples in each experimental group used in this report
Experimental group Number of samples in group
male_0 10
male_2 10
male_1000 10
male_5000 7

2.5.2 Samples removed from this report

This table shows the samples that were removed from this analysis.

# Conditionally sort by dose if that column name is present?
knitr::kable(DESeqDesignAsRead %>% arrange(!!sym(params$design)) %>% dplyr::filter(!original_names %in% DESeqDesign$original_names),
             row.names =  F,
             caption="Samples removed from analysis") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive")) %>%
  scroll_box(width = "100%", height = "480px")
Samples removed from analysis
name sex dose files group original_names
female_0_bc41 female 0 bc41 female_0 SRR5890392
female_0_bc42 female 0 bc42 female_0 SRR5890389
female_0_bc43 female 0 bc43 female_0 SRR5890396
female_0_bc44 female 0 bc44 female_0 SRR5890393
female_0_bc45 female 0 bc45 female_0 SRR5890429
female_0_bc46 female 0 bc46 female_0 SRR5890370
female_0_bc47 female 0 bc47 female_0 SRR5890387
female_0_bc48 female 0 bc48 female_0 SRR5890381
female_0_bc49 female 0 bc49 female_0 SRR5890371
female_0_bc50 female 0 bc50 female_0 SRR5890391
female_1000_bc61 female 1000 bc61 female_1000 SRR5890388
female_1000_bc62 female 1000 bc62 female_1000 SRR5890431
female_1000_bc63 female 1000 bc63 female_1000 SRR5890386
female_1000_bc64 female 1000 bc64 female_1000 SRR5890440
female_1000_bc65 female 1000 bc65 female_1000 SRR5890384
female_1000_bc66 female 1000 bc66 female_1000 SRR5890405
female_1000_bc67 female 1000 bc67 female_1000 SRR5890382
female_1000_bc68 female 1000 bc68 female_1000 SRR5890362
female_1000_bc69 female 1000 bc69 female_1000 SRR5890394
female_1000_bc70 female 1000 bc70 female_1000 SRR5890390
female_2_bc51 female 2 bc51 female_2 SRR5890421
female_2_bc52 female 2 bc52 female_2 SRR5890422
female_2_bc53 female 2 bc53 female_2 SRR5890383
female_2_bc54 female 2 bc54 female_2 SRR5890423
female_2_bc55 female 2 bc55 female_2 SRR5890363
female_2_bc56 female 2 bc56 female_2 SRR5890385
female_2_bc57 female 2 bc57 female_2 SRR5890415
female_2_bc58 female 2 bc58 female_2 SRR5890416
female_2_bc59 female 2 bc59 female_2 SRR5890395
female_2_bc60 female 2 bc60 female_2 SRR5890434
female_5000_bc71 female 5000 bc71 female_5000 SRR5890372
female_5000_bc72 female 5000 bc72 female_5000 SRR5890424
female_5000_bc73 female 5000 bc73 female_5000 SRR5890425
female_5000_bc74 female 5000 bc74 female_5000 SRR5890426
female_5000_bc75 female 5000 bc75 female_5000 SRR5890366
female_5000_bc76 female 5000 bc76 female_5000 SRR5890428
female_5000_bc77 female 5000 bc77 female_5000 SRR5890406
female_5000_bc78 female 5000 bc78 female_5000 SRR5890430
female_5000_bc79 female 5000 bc79 female_5000 SRR5890437
female_5000_bc80 female 5000 bc80 female_5000 SRR5890432
male_5000_bc31 male 5000 bc31 male_5000 SRR5890439
male_5000_bc38 male 5000 bc38 male_5000 SRR5890438
male_5000_bc39 male 5000 bc39 male_5000 SRR5890419
knitr::kable(DESeqDesignAsRead %>%
               dplyr::filter(original_names %in% removed) %>%
               group_by(!!sym(params$design)) %>% tally(),
             row.names = F,
             col.names = c("Experimental group", "Number of samples removed"),
             caption="Number of samples removed from each experimental group") %>%
  kable_styling(bootstrap_options = "striped", full_width = F, position = "left") %>%
  scroll_box(width = "100%", height = "480px")
Number of samples removed from each experimental group
Experimental group Number of samples removed
female_0 10
female_1000 10
female_2 10
female_5000 9

2.5.3 Original sample metadata as provided

This table shows the original sample metadata including any samples that were removed within this report.

# Conditionally sort by dose if that column name is present?
knitr::kable(DESeqDesignAsRead %>% arrange(!!sym(params$design)),
             row.names =  F,
             caption="Complete sample metadata as provided to the Genomics Laboratory.") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive")) %>%
  scroll_box(width = "100%", height = "480px")
Complete sample metadata as provided to the Genomics Laboratory.
name sex dose files group original_names
female_0_bc41 female 0 bc41 female_0 SRR5890392
female_0_bc42 female 0 bc42 female_0 SRR5890389
female_0_bc43 female 0 bc43 female_0 SRR5890396
female_0_bc44 female 0 bc44 female_0 SRR5890393
female_0_bc45 female 0 bc45 female_0 SRR5890429
female_0_bc46 female 0 bc46 female_0 SRR5890370
female_0_bc47 female 0 bc47 female_0 SRR5890387
female_0_bc48 female 0 bc48 female_0 SRR5890381
female_0_bc49 female 0 bc49 female_0 SRR5890371
female_0_bc50 female 0 bc50 female_0 SRR5890391
female_1000_bc61 female 1000 bc61 female_1000 SRR5890388
female_1000_bc62 female 1000 bc62 female_1000 SRR5890431
female_1000_bc63 female 1000 bc63 female_1000 SRR5890386
female_1000_bc64 female 1000 bc64 female_1000 SRR5890440
female_1000_bc65 female 1000 bc65 female_1000 SRR5890384
female_1000_bc66 female 1000 bc66 female_1000 SRR5890405
female_1000_bc67 female 1000 bc67 female_1000 SRR5890382
female_1000_bc68 female 1000 bc68 female_1000 SRR5890362
female_1000_bc69 female 1000 bc69 female_1000 SRR5890394
female_1000_bc70 female 1000 bc70 female_1000 SRR5890390
female_2_bc51 female 2 bc51 female_2 SRR5890421
female_2_bc52 female 2 bc52 female_2 SRR5890422
female_2_bc53 female 2 bc53 female_2 SRR5890383
female_2_bc54 female 2 bc54 female_2 SRR5890423
female_2_bc55 female 2 bc55 female_2 SRR5890363
female_2_bc56 female 2 bc56 female_2 SRR5890385
female_2_bc57 female 2 bc57 female_2 SRR5890415
female_2_bc58 female 2 bc58 female_2 SRR5890416
female_2_bc59 female 2 bc59 female_2 SRR5890395
female_2_bc60 female 2 bc60 female_2 SRR5890434
female_5000_bc71 female 5000 bc71 female_5000 SRR5890372
female_5000_bc72 female 5000 bc72 female_5000 SRR5890424
female_5000_bc73 female 5000 bc73 female_5000 SRR5890425
female_5000_bc74 female 5000 bc74 female_5000 SRR5890426
female_5000_bc75 female 5000 bc75 female_5000 SRR5890366
female_5000_bc76 female 5000 bc76 female_5000 SRR5890428
female_5000_bc77 female 5000 bc77 female_5000 SRR5890406
female_5000_bc78 female 5000 bc78 female_5000 SRR5890430
female_5000_bc79 female 5000 bc79 female_5000 SRR5890437
female_5000_bc80 female 5000 bc80 female_5000 SRR5890432
male_0_bc1 male 0 bc01 male_0 SRR5890399
male_0_bc2 male 0 bc02 male_0 SRR5890400
male_0_bc3 male 0 bc03 male_0 SRR5890397
male_0_bc4 male 0 bc04 male_0 SRR5890398
male_0_bc5 male 0 bc05 male_0 SRR5890403
male_0_bc6 male 0 bc06 male_0 SRR5890404
male_0_bc7 male 0 bc07 male_0 SRR5890401
male_0_bc8 male 0 bc08 male_0 SRR5890402
male_0_bc9 male 0 bc09 male_0 SRR5890436
male_0_bc10 male 0 bc10 male_0 SRR5890367
male_1000_bc21 male 1000 bc21 male_1000 SRR5890376
male_1000_bc22 male 1000 bc22 male_1000 SRR5890375
male_1000_bc23 male 1000 bc23 male_1000 SRR5890374
male_1000_bc24 male 1000 bc24 male_1000 SRR5890373
male_1000_bc25 male 1000 bc25 male_1000 SRR5890380
male_1000_bc26 male 1000 bc26 male_1000 SRR5890379
male_1000_bc27 male 1000 bc27 male_1000 SRR5890378
male_1000_bc28 male 1000 bc28 male_1000 SRR5890377
male_1000_bc29 male 1000 bc29 male_1000 SRR5890369
male_1000_bc30 male 1000 bc30 male_1000 SRR5890368
male_2_bc11 male 2 bc11 male_2 SRR5890409
male_2_bc12 male 2 bc12 male_2 SRR5890410
male_2_bc13 male 2 bc13 male_2 SRR5890407
male_2_bc14 male 2 bc14 male_2 SRR5890408
male_2_bc15 male 2 bc15 male_2 SRR5890413
male_2_bc16 male 2 bc16 male_2 SRR5890414
male_2_bc17 male 2 bc17 male_2 SRR5890411
male_2_bc18 male 2 bc18 male_2 SRR5890412
male_2_bc19 male 2 bc19 male_2 SRR5890417
male_2_bc20 male 2 bc20 male_2 SRR5890418
male_5000_bc31 male 5000 bc31 male_5000 SRR5890439
male_5000_bc32 male 5000 bc32 male_5000 SRR5890361
male_5000_bc33 male 5000 bc33 male_5000 SRR5890364
male_5000_bc34 male 5000 bc34 male_5000 SRR5890365
male_5000_bc35 male 5000 bc35 male_5000 SRR5890427
male_5000_bc36 male 5000 bc36 male_5000 SRR5890433
male_5000_bc37 male 5000 bc37 male_5000 SRR5890435
male_5000_bc38 male 5000 bc38 male_5000 SRR5890438
male_5000_bc39 male 5000 bc39 male_5000 SRR5890419
male_5000_bc40 male 5000 bc40 male_5000 SRR5890420
knitr::kable(DESeqDesignAsRead %>% group_by(!!sym(params$design)) %>% tally(),
             row.names = F,
             col.names = c("Experimental group", "Number of samples as provided"),
             caption="Number of samples and experimental groups as provided") %>%
  kable_styling(bootstrap_options = "striped", full_width = F, position = "left") %>%
  scroll_box(width = "100%", height = "480px")
Number of samples and experimental groups as provided
Experimental group Number of samples as provided
female_0 10
female_1000 10
female_2 10
female_5000 10
male_0 10
male_1000 10
male_2 10
male_5000 10

2.6 Load R-ODAF functions

###################################################################################
#DEFINE FUNCTIONS
###################################################################################
plot.barplots<-function(samples,b) {
  color <- NULL
  for (h in 1:ncol(norm_data)){
    if (substring(colnames(norm_data)[h], 1, 3) == substring(condition2, 1, 3)) { color <- c(color, "red3") } else { color <- c(color, "darkgrey")}
  }
  fileNamePlot <- paste0(b, row.names(samples)[b], ".png")
  pseudoTitle <- paste0(row.names(samples)[b], "_pAdj:", samples[b,"padj"])
  
  png(file=paste(fileNamePlot, sep="/"), width=1200, height=700, pointsize=20)
  par(mar=c(8,4,3,1))
  barplot(as.numeric(norm_data[row.names(samples)[b],]), las=2, col=color, main=pseudoTitle, cex.names=0.5,  cex.axis=0.8, names.arg=colnames(norm_data)) 
  dev.off()
} #plot.barplots function done

###################################################################################
draw.barplots<-function(samples, top_bottom, NUM){
  if (nrow(samples) == 0) {
    #print("no genes to plot") 
  } else { 
    if (top_bottom == "top") {
      #print(paste0("drawing Top ", NUM, " plots"))
      if (nrow(samples) <= NUM) { 
        for (b in 1:nrow(samples)) {plot.barplots(samples,b)}
      }
      
      if (nrow(samples) > NUM) { 
        for (b in 1:NUM) {plot.barplots(samples,b)} 
      } 
    }
    
    if (top_bottom == "bottom") {
      #print(paste0("drawing Bottom", NUM, " plots"))
      if (nrow(samples) <= NUM) { 
        for (b in 1:nrow(samples)) {plot.barplots(samples,b)}
      }
      if (nrow(samples) > NUM) { 
        for (b in ((nrow(samples)-NUM+1):nrow(samples))) {plot.barplots(DEsamples,b)}
      }
    }}
} #draw.barplots function done

###################################################################################
###################################################################################

The code above (not shown by default) loads in plotting functions specific to the Omics Data Analysis Frameworks for Regulatory application (R-ODAF) template. More information on the R-ODAF framework is available here.

2.7 Run pairwise comparisons


# Initial setup for DESeq2 contrasts
cooks   <- FALSE # Normally set Cook's cutoff to false
resList <- list()
Counts  <- counts(dds, normalized=TRUE)
CPMdds  <- cpm(counts(dds, normalized=TRUE))

for (x in 1:nrow(contrasts)) {  ## for all comparisons to be done   
    
  condition1 <- contrasts[x,2]
    condition2 <- contrasts[x,1]
    print(paste(condition2, " vs ", condition1, ":", NORM_TYPE))
    
    DE_Design <- matrix(data=NA, ncol=2)
    DE_Design <- as.matrix(DESeqDesign[c(grep(condition1,DESeqDesign[,DESIGN]), grep(condition2,DESeqDesign[,DESIGN])),])
    samples   <- sampleData[, rownames(DE_Design)]
    colnames(samples) <- NULL
    
    ###########

    print(paste0("Filtering genes: 75% of at least 1 group need to be above ", MinCount, " CPM"))
    print("AND")
    print("Detecting spurious spikes: Max-Median > Sum/(Rep+1)" )
        SampPerGroup <- table(DE_Design[,DESIGN])
        idx          <- FlagSpike <- NameRows <- NULL
        for (gene in 1:nrow(dds)) { # Loop over each gene
            GroupsPass <- checkSpike <- NULL
            for (group in 1:length(SampPerGroup)) { # Test if group passes
                sampleCols <- grep(dimnames(SampPerGroup)[[1]][group], DE_Design[,DESIGN])
                Check      <- sum(CPMdds[gene, sampleCols] >= MinCount) >= 0.75*SampPerGroup[group]
                GroupsPass <- c(GroupsPass, Check)
                if (Check == FALSE) {checkSpike <- c(checkSpike, Check)} else {
                    checkSpike <- c(checkSpike, ((max(Counts[gene, sampleCols]) - median(Counts[gene,sampleCols])) >=
                                                 (sum(Counts[gene, sampleCols]) / (SampPerGroup[group]+1))))
                }
            }
            idx <- c(idx, as.logical(sum(GroupsPass)))
            if (sum(checkSpike) >=1) {
                FlagSpike <- rbind(FlagSpike, Counts[gene,])
                NameRows  <<- c(NameRows, row.names(Counts)[gene])
                row.names(FlagSpike) <- NameRows 
            }
        }

    print("Obtaining the DESeq2 results")
    currentContrast <- c(DESIGN, condition2, condition1)
    res <- results(dds[idx],
                   parallel = TRUE, BPPARAM=MulticoreParam(39),
                   contrast=currentContrast,
                   pAdjustMethod= 'fdr',
                   cooksCutoff=cooks) # Cooks cutoff disabled - manually inspect.
    res <- lfcShrink(dds=dds[idx],
                     contrast=currentContrast,
                     res=res,
                     type="ashr")
    #Make new directory for the ODAF-specific files
    ODAFdir <- file.path(paths$DEG_output, "R-ODAF")
    if(!dir.exists(ODAFdir)) {dir.create(ODAFdir, recursive = TRUE)}
    setwd(ODAFdir)
    FileName <- paste(NORM_TYPE, condition2,"vs",condition1, "FDR", pAdjValue, sep="_")     
    #Save output tables     
    norm_data <<- counts(dds[idx],normalized=TRUE)
    colnames(norm_data) <- colData(dds)[,DESIGN]
    write.table(norm_data,file=paste0(FileName, "_Norm_Data.txt"), sep="\t", quote=FALSE)
    write.table(FlagSpike,file=paste0(FileName, "_FlaggedSpikes.txt"), sep="\t", quote=FALSE)
    DEsamples <<- subset(res,res$padj < pAdjValue)  
    write.table(DEsamples,file=paste0(FileName,"_DEG_table.txt"), sep="\t", quote=FALSE)
    DEspikes <<- DEsamples[rownames(DEsamples)%in%NameRows,]    
    write.table(DEspikes,file=paste0(FileName,"_DEspikes_table.txt"), sep="\t",quote=FALSE)

    resList[[x]] <- res
    
    if (R_ODAF_plots==TRUE) {
    print("creating Read count Plots")
    # top DEGs
    plotdir <- file.path(paths$DEG_output, "plots")
    if(!dir.exists(plotdir)) {dir.create(plotdir, recursive = TRUE)}
    barplot.dir <- file.path(paths$DEG_output, "plots", "/barplot_genes/")
    if(!dir.exists(barplot.dir)) {dir.create(barplot.dir, recursive = TRUE)}
  
    TOPbarplot.dir <- file.path(barplot.dir, "Top_DEGs")
    if(!dir.exists(TOPbarplot.dir)) {dir.create(TOPbarplot.dir, recursive = TRUE)}
    setwd(TOPbarplot.dir)
    draw.barplots(DEsamples, "top", 20) # (DEsamples, top_bottom, NUM)
    print("Top 20 DEG plots done")
  
    # Spurious spikes
    SPIKEbarplot.dir <- file.path(barplot.dir, "DE_Spurious_spikes")
    if(!dir.exists(SPIKEbarplot.dir)) {dir.create(SPIKEbarplot.dir, recursive = TRUE)}
    setwd(SPIKEbarplot.dir)
    draw.barplots(DEspikes, "top", nrow(DEspikes)) # (DEsamples, top_bottom, NUM)
    print("All DE_Spurious_spike plots done")
    }
}
## [1] "male_2  vs  male_0 : 2020_Flame_retardants_male_DESeq2_RNA-Seq"
## [1] "Filtering genes: 75% of at least 1 group need to be above 1 CPM"
## [1] "AND"
## [1] "Detecting spurious spikes: Max-Median > Sum/(Rep+1)"
## [1] "Obtaining the DESeq2 results"
## [1] "male_1000  vs  male_0 : 2020_Flame_retardants_male_DESeq2_RNA-Seq"
## [1] "Filtering genes: 75% of at least 1 group need to be above 1 CPM"
## [1] "AND"
## [1] "Detecting spurious spikes: Max-Median > Sum/(Rep+1)"
## [1] "Obtaining the DESeq2 results"
## [1] "male_5000  vs  male_0 : 2020_Flame_retardants_male_DESeq2_RNA-Seq"
## [1] "Filtering genes: 75% of at least 1 group need to be above 1 CPM"
## [1] "AND"
## [1] "Detecting spurious spikes: Max-Median > Sum/(Rep+1)"
## [1] "Obtaining the DESeq2 results"

The code above (not shown by default) runs the R-ODAF spurious spike detection and outputs the DESeq Results objects as a list for each contrast. As specified by the R-ODAF guidelines, 75% of at least 1 group need to be above 1 CPM and spurious spikes were removed in which Max-Median > Sum/(Rep+1).

The log2FoldChange shrinkage procedure used was ashr. An alpha of 0.1 was used to extract raw results, which are reported as the Wald test p-value. To account for multiple testing, fdr adjusted p-values are reported. Cook’s cutoff was set to FALSE in this analysis.

2.8 Create summary tables

#listEnsembl()
#listMarts()
#listDatasets(useMart('ensembl'))
ensembl <- useMart("ensembl", dataset = ensembl_species,  host = "useast.ensembl.org")
#listAttributes(ensembl)
#listFilters(ensembl)

genes <- row.names(resList[[1]])
#genes_all <- unique(row.names(assay(dds)))

if (file.exists(file.path(paths$RData,"id_table.Rdata"))) {
  print(paste("Already found ID table; skipping biomaRt query and loading from disk."))
  load(file.path(paths$RData,"id_table.Rdata"))
  id_table <- id_table_entrez[,1:3]
  } else {
  id_table_entrez <- getBM(filters=biomart_filter,
                        attributes= c("ensembl_gene_id",
                                      "external_gene_name", # mgi_symbol for Mouse
                                      "description",
                                      "entrezgene_id",
                                      "entrezgene_accession"),
                        values=genes,
                        mart=ensembl)
  save(id_table_entrez, file=file.path(paths$RData,"id_table.Rdata"))
  id_table <- id_table_entrez[,1:3]
}
## [1] "Already found ID table; skipping biomaRt query and loading from disk."

# genes_entrezid <- dplyr::left_join(data.frame(genes), id_table_entrez, by=c("genes" = biomart_filter)) # Can I remove this? I think so

sigtabList <- list()
alltablList <- list()
for (i in 1:length(resList)) {
  print(i)
  sigTab <- resList[[i]]
  # Add taxonomy
  if (nrow(sigTab) == 0) {
    next
  } else {
    sigTab <- cbind(genes=row.names(resList[[i]]),
                    as(sigTab, "data.frame"),
                    contrast = gsub(pattern = paste0("log2.*", DESIGN, "\ "),
                                    replacement =  "",
                                    x = resList[[i]]@elementMetadata[[2]][2]))
    names(sigTab)[names(sigTab) == "genes"] <- biomart_filter
    sigTab <- dplyr::left_join(sigTab,
                               id_table,
                               by=biomart_filter)
    sigTab <- dplyr::mutate(sigTab, linearFoldChange=ifelse(log2FoldChange > 0,
                                                            2 ^ log2FoldChange,
                                                            -1 / (2 ^ log2FoldChange)))
    #sigTab <- sigTab[,c(1,9,10,2,3,11,5:8)] # Reorder columns
    sigTab <- sigTab[,c(1,8,9,2,3,10,4:7)]
    alltablList[[i]] <- sigTab
    sigTab <- sigTab[!is.na(sigTab$padj) & sigTab$padj < alpha & abs(sigTab$linearFoldChange) > linear_fc_filter, ] ## FILTERS!
    sigtabList[[i]] <- sigTab %>% dplyr::distinct()
  }
}
## [1] 1
## [1] 2
## [1] 3
# Write dataframe of all results
# Significant only
sigtabList <- sigtabList[!sapply(sigtabList, is.null)]
significantResults <- rbindlist(sigtabList)
significantResults <- dplyr::distinct(significantResults)
significantResults$contrast <- factor(significantResults$contrast)
# All results including non-significant
allResults <- rbindlist(alltablList)

# All Results for Plotting
# Replace NA gene symbols with ensembl ID
allResults$external_gene_name[is.na(allResults$external_gene_name)] <- allResults$ensembl_gene_id[is.na(allResults$external_gene_name)] 
# Replace blank gene symbols with ensembl ID
allResults$external_gene_name[allResults$external_gene_name == ""]  <- allResults$ensembl_gene_id[allResults$external_gene_name == ""]

IPA <- allResults %>% 
  distinct() %>% 
  pivot_wider(names_from = contrast, values_from = c(log2FoldChange,linearFoldChange,lfcSE,pvalue,padj))
allResults$padj[allResults$padj == 0] <- 10^-100 # For plotting purposes!
allResultsOrdered_logFC_filter <- dplyr::filter(allResults, abs(linearFoldChange) > 1.5) %>%
  arrange(-abs(linearFoldChange))
allResultsOrdered_logFC_filter <- dplyr::filter(allResultsOrdered_logFC_filter, padj < alpha)
res.df <- allResultsOrdered_logFC_filter

degTable <- significantResults %>% 
  dplyr::group_by(contrast) %>%
  dplyr::count()

lengths <- lapply(resList, nrow)
longest <- which.max(lengths)
summaryTable <- data.frame( genes=row.names(resList[[longest]]),
                            baseMean=resList[[longest]]$baseMean )
names(summaryTable)[names(summaryTable) == "genes"] <- biomart_filter
contrastsInSummary <- vector()
for (i in 1:length(resList)) {
  print(i)
  n <- resList[[i]]@elementMetadata[[2]][2]
  n <- gsub(pattern = paste0("log2\ fold\ change\ \\(MMSE\\):\ ",DESIGN), # Might need to be MLE for some tests
            replacement =  paste0("log2 Fold Change"),
            x = resList[[i]]@elementMetadata[[2]][2])
  p <- gsub(pattern = paste0("log2\ fold\ change\ \\(MMSE\\):\ ",DESIGN),
           replacement =  resList[[i]]@elementMetadata[[2]][6], # Not used?
           x = resList[[i]]@elementMetadata[[2]][2])
  q <- gsub(pattern = paste0("log2\ fold\ change\ \\(MMSE\\):\ ",DESIGN,"\ "),
            replacement =  "",
            x = resList[[i]]@elementMetadata[[2]][2])
  message(n)
  message(p)
  message(q)
  toJoin <- as.data.frame(resList[[i]])
  setDT(toJoin, keep.rownames = T)[]
  setnames(toJoin, 1, biomart_filter)
  toJoin <- mutate(toJoin, linearFoldChange=ifelse(log2FoldChange > 0,
                                                2 ^ log2FoldChange,
                                                -1 / (2 ^ log2FoldChange)))
  toJoin <- toJoin[,c(1:3,7,4:6)]
  summaryTable <- dplyr::left_join(summaryTable, dplyr::select(toJoin, !c(baseMean,pvalue,lfcSE)), by=biomart_filter)

  names(summaryTable)[[ncol(summaryTable)-2]] <- paste0("log2FoldChange_",i)
  names(summaryTable)[[ncol(summaryTable)-1]] <- paste0("linearFoldChange_",i)
  names(summaryTable)[[ncol(summaryTable)]] <- paste0("FDR_",i)
  contrastsInSummary[i] <- q
  print(summary(resList[[i]], pAdjValue))
}
## [1] 1
## 
## out of 10514 with nonzero total read count
## adjusted p-value < 0.05
## LFC > 0 (up)       : 6, 0.057%
## LFC < 0 (down)     : 6, 0.057%
## outliers [1]       : 0, 0%
## low counts [2]     : 0, 0%
## (mean count < 3)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results
## 
## NULL
## [1] 2
## 
## out of 10514 with nonzero total read count
## adjusted p-value < 0.05
## LFC > 0 (up)       : 30, 0.29%
## LFC < 0 (down)     : 18, 0.17%
## outliers [1]       : 0, 0%
## low counts [2]     : 0, 0%
## (mean count < 3)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results
## 
## NULL
## [1] 3
## 
## out of 10495 with nonzero total read count
## adjusted p-value < 0.05
## LFC > 0 (up)       : 189, 1.8%
## LFC < 0 (down)     : 280, 2.7%
## outliers [1]       : 0, 0%
## low counts [2]     : 0, 0%
## (mean count < 3)
## [1] see 'cooksCutoff' argument of ?results
## [2] see 'independentFiltering' argument of ?results
## 
## NULL

maxFCs <- allResults %>%
  dplyr::group_by(!!sym(biomart_filter)) %>%
  dplyr::filter(abs(linearFoldChange) == max(abs(linearFoldChange))) %>%
  dplyr::ungroup() %>%
  dplyr::select(!!sym(biomart_filter), linearFoldChange)

minPvals <- allResults %>%
  group_by(!!sym(biomart_filter)) %>%
  dplyr::filter(padj == min(padj)) %>%
  dplyr::ungroup() %>%
  dplyr::select(!!sym(biomart_filter), padj)

summaryTable <- summaryTable %>%
  left_join(id_table, by=biomart_filter) %>%
  left_join(maxFCs, by=biomart_filter) %>%
  left_join(minPvals, by=biomart_filter) %>%
  dplyr::rename(maxFoldChange = linearFoldChange,
         minFDR_pval = padj) %>%
  dplyr::distinct() %>%
  mutate(maxFoldChange=abs(maxFoldChange)) # This eliminates the direction of change: this way it's easy to sort.

numColsToPrepend <- ncol(summaryTable) - 3*length(resList) - 2 # Number of columns per contrast = 3. Subtract two for the baseMean and genes columns.
colPositionsToPrependSTART <- ncol(summaryTable) - numColsToPrepend + 1
colPositionsOfData <- ncol(summaryTable) - numColsToPrepend
summaryTable <- summaryTable[,c(1,
                                colPositionsToPrependSTART:ncol(summaryTable),
                                2:colPositionsOfData)]

CPMddsDF <- data.frame(genes = row.names(CPMdds), CPMdds, check.names=F)
CPMddsDF <- dplyr::left_join(CPMddsDF, id_table, by=c("genes" = biomart_filter))
numColsToPrepend <- ncol(CPMddsDF) - ncol(CPMdds) - 1
colPositionsToPrependSTART <- ncol(CPMddsDF) - numColsToPrepend + 1
colPositionsOfData <- ncol(CPMddsDF) - numColsToPrepend
CPMddsDF <- CPMddsDF[,c(1,colPositionsToPrependSTART:ncol(CPMddsDF),2:colPositionsOfData)]

The code above (not shown by default) generates tables summarizing the differentially expressed genes (DEGs). A linear fold change cutoff of 1.5 and adjusted p-value of 0.05 was used to filter the results.

Here is the number of DEGs in each group:

kable(degTable,
      caption="Number of differentially expressed genes across each contrast") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"))
Number of differentially expressed genes across each contrast
contrast n
male_1000 vs male_0 24
male_2 vs male_0 9
male_5000 vs male_0 63
#######################################
### Write results table from DESeq2
#######################################
setwd(paths$DEG_output)
write.table(allResults,         file=paste0(NORM_TYPE,"-DESeq_output_ALL.txt"),               quote=F, sep='\t', col.names=NA)
write.table(significantResults, file=paste0(NORM_TYPE,"-DESeq_output_significant.txt"),       quote=F, sep='\t', col.names=NA)
write.table(summaryTable,       file=paste0(NORM_TYPE,"-DESeq_output_all_genes.txt"),         quote=F, sep='\t', col.names=NA)
write.table(CPMddsDF,           file=paste0(NORM_TYPE,"-Per_sample_CPM.txt"),                 quote=F, sep='\t', col.names=NA)
write.table(Counts,             file=paste0(NORM_TYPE,"-Per_sample_normalized_counts.txt"),   quote=F, sep='\t', col.names=NA)

The code above (not shown by default) writes text files for each DEG summary type.

3 Write data

setwd(paths$DEG_output)
#######################################
### Write results above but in Excel
#######################################
### Global options
options("openxlsx.borderColour" = "#4F80BD")
options("openxlsx.borderStyle" = "thin")
options("openxlsx.maxWidth" = 50)
hs1 <- createStyle(textDecoration = "Bold",
                   border = "Bottom",
                   fontColour = "black")
hs2 <- createStyle(textDecoration = "Bold",
                   border = c("top", "bottom", "left", "right"),
                   fontColour = "black",
                   fgFill="#C5D9F1")

### Summary results - one gene per line, columns are contrasts
wb1 <- createWorkbook()
modifyBaseFont(wb1, fontSize = 10, fontName = "Arial Narrow")
addWorksheet(wb1, "DESeq_results_per_gene")
for (j in 1:length(contrastsInSummary)) {
  myStartcol=7+((j-1)*3)
  myEndcol=9+((j-1)*3)
  mergeCells(wb1,
             sheet = 1,
             cols = myStartcol:myEndcol,
             rows = 1)
  writeData(
    wb1,
    sheet = 1,
    x = contrastsInSummary[j],
    startCol = myStartcol,
    startRow = 1)
}
conditionalFormatting(wb1,
                      sheet = 1,
                      rows = 1,
                      cols = 1:ncol(summaryTable),
                      type = "contains",
                      rule = "",
                      style=hs2)
freezePane(wb1, sheet = 1, firstActiveRow = 3, firstActiveCol = 4)
writeDataTable(wb1,
               sheet = 1,
               startRow = 2,
               x = summaryTable,
               colNames = TRUE,
               rowNames = F,
               tableStyle = "none",
               headerStyle = hs1,
               keepNA = T,
               na.string = "NA")
setColWidths(wb1, sheet = 1, cols = 1:6, widths = "auto") # This is hard-coded, so prone to error; will only impact auto adjustment of col widths.
setColWidths(wb1, sheet = 1, cols = 7:ncol(summaryTable), widths = 13) # This is hard-coded, so prone to error; will only impact auto adjustment of col widths.
fname1 <- paste0("1.",NORM_TYPE,"-DESeq_by_gene.xlsx")
saveWorkbook(wb1, fname1, overwrite = TRUE)

### All results in one table
wb2 <- createWorkbook()
modifyBaseFont(wb2, fontSize = 10, fontName = "Arial Narrow")
addWorksheet(wb2, paste0("FDR",pAdjValue,".Linear.FC.",linear_fc_filter))
freezePane(wb2, sheet = 1, firstRow = TRUE, firstActiveCol = 4)
writeDataTable(wb2,
               sheet = 1,
               x = significantResults,
               colNames = TRUE,
               rowNames = F,
               tableStyle = "none",
               headerStyle = hs1,
               keepNA = T,
               na.string = "NA")
setColWidths(wb2, sheet = 1, cols = 1:ncol(significantResults), widths = "auto")
addWorksheet(wb2, "DESeq_all_results")
freezePane(wb2, sheet = 2, firstRow = TRUE, firstActiveCol = 4)
writeDataTable(wb2,
               sheet = 2,
               x = allResults,
               colNames = TRUE,
               rowNames = F,
               tableStyle = "none",
               headerStyle = hs1,
               keepNA = T,
               na.string = "NA")
setColWidths(wb2, sheet = 2, cols = 1:ncol(allResults), widths = "auto")
saveWorkbook(wb2, paste0("2.",NORM_TYPE,"-DESeq_all.xlsx"), overwrite = TRUE)

### All results with different tabs for each contrast
wb3 <- createWorkbook()
modifyBaseFont(wb3, fontSize = 10, fontName = "Arial Narrow")

short_contrast_names <- stringr::str_trunc(short_contrast_names, 30, side="center")

for (i in 1:length(levels(factor(allResults$contrast)))) {
  print(i)
  dataToWrite <- allResults[allResults$contrast==levels(factor(allResults$contrast))[i],]
  addWorksheet(wb3, short_contrast_names[i])
  freezePane(wb3, sheet = i, firstRow = TRUE, firstActiveCol = 4)
  writeDataTable(wb3,
                 sheet = i,
                 x = dataToWrite,
                 colNames = TRUE,
                 rowNames = F,
                 tableStyle = "none",
                 headerStyle = hs1,
                 keepNA = T,
                 na.string = "NA")
  setColWidths(wb3, sheet = i, cols = 1:ncol(dataToWrite), widths = "auto")
}
## [1] 1
## [1] 2
## [1] 3
saveWorkbook(wb3, paste0("3.",NORM_TYPE,"-DESeq_by_contrast.xlsx"), overwrite = TRUE)

### CPM
wb4 <- createWorkbook()
modifyBaseFont(wb4, fontSize = 10, fontName = "Arial Narrow")
addWorksheet(wb4, "Counts per million")
freezePane(wb4, sheet = 1, firstRow = TRUE, firstActiveCol = 4)
writeDataTable(wb4,
               sheet = 1,
               x = as.data.frame(CPMddsDF),
               colNames = TRUE,
               rowNames = F,
               tableStyle = "none",
               headerStyle = hs1,
               keepNA = T,
               na.string = "NA")
setColWidths(wb4, sheet = 1, cols = 1:ncol(CPMddsDF), widths = "auto")
saveWorkbook(wb4, paste0("4.",NORM_TYPE,"-CPM.xlsx"), overwrite = TRUE)

### IPA
wb5 <- createWorkbook()
modifyBaseFont(wb5, fontSize = 10, fontName = "Arial Narrow")
addWorksheet(wb5, "For IPA upload")
freezePane(wb5, sheet = 1, firstRow = TRUE, firstActiveCol = 5)
writeDataTable(wb5,
               sheet = 1,
               x = as.data.frame(IPA),
               colNames = TRUE,
               rowNames = F,
               tableStyle = "none",
               headerStyle = hs1,
               keepNA = T,
               na.string = "NA")
setColWidths(wb5, sheet = 1, cols = 1:ncol(IPA), widths = "auto")
saveWorkbook(wb5, paste0("5.",NORM_TYPE,"-IPA.xlsx"), overwrite = TRUE)

The code above (not shown by default) writes Excel workbooks and text files of DEG lists.

These files should be provided to you as a separate zip file.

setwd(paths$DEG_output)
xfun::embed_files(list.files(".", "[.](xlsx)$"), name = "RNASeq_Spreadsheets.zip")
setwd(paths$DEG_output)
xfun::embed_files(list.files(".", "[.](txt)$"), name = "RNASeq_Text_Files.zip")
setwd(paths$DEG_output)
xfun::embed_dir(ODAFdir, name = "RNASeq_R-ODAF_text_files.zip")
# Save RData here if flag is TRUE.
if(is.na(params$group_facet)) {
  save.image(file = file.path(paths$RData, "Partial_analysis.RData"))
} else {
  save.image(file = file.path(paths$RData, paste0("Partial_analysis_", params$group_filter, ".RData")))
}
# Add a flag to the start of the script with if statements in all code chunks to check if the flag is set.
# If flag is TRUE, run analysis; if flag is FALSE, SKIP analysis and continue here (just to modify plotting output).

4 PCA Plots

4.1 All genes, before filtering

## Perform PCA analysis and make plot
plotPCA(rld, intgroup = params$intgroup, ntop=nrow(assay(rld))) + 
  theme(legend.position = "bottom") +
  guides(fill=guide_legend(ncol=3))

## Get percent of variance explained
data_pca <- plotPCA(rld, intgroup = params$intgroup, ntop=nrow(assay(rld)), returnData = TRUE)
percentVar <- round(100 * attr(data_pca, "percentVar"))

This plot shows the first two principal components that explain the variability in the data using the regularized log count data. If you are unfamiliar with principal component analysis, you might want to check the Wikipedia entry or this interactive explanation. In this case, the first and second principal component explain 12 and 10 percent of the variance respectively.

4.2 DEGs only

rld_degs <- rld[row.names(assay(rld)) %in% significantResults[[biomart_filter]],]  # d[ d[[THECOLUMN]] == someValue , ]

## Perform PCA analysis and make plot
plotPCA(rld_degs, intgroup = params$intgroup, ntop=nrow(rld_degs)) +
  theme(legend.position = "bottom") +
  guides(fill=guide_legend(ncol=3))

PCAplotDEGs <- plotPCA(rld_degs, intgroup = params$intgroup, ntop=nrow(rld_degs), returnData = TRUE)

## Get percent of variance explained
percentVarDEGs <- round(100 * attr(PCAplotDEGs, "percentVar"))

This plot shows the principal components analysis limited to all DEGs. In this case, the first and second principal component explain 50 and 14 percent of the variance respectively.

4.3 Top 100 most variable genes only

# Run this code only once for both the PCA and clustering analysis
rv = rowVars(assay(rld))
select = order(rv, decreasing = TRUE)[1:nBest]

rld_top <- rld[select,]

## Perform PCA analysis and make plot
plotPCA(rld_top, intgroup = params$intgroup, ntop=nrow(rld_top)) + 
  theme(legend.position = "bottom") +
  guides(fill=guide_legend(ncol=3))

## Get percent of variance explained
data_pcaTop <- plotPCA(rld_top, intgroup = params$intgroup, returnData = TRUE, ntop=nrow(rld_top))
percentVarTop <- round(100 * attr(data_pcaTop, "percentVar"))

This plot shows the principal components analysis limited to all DEGs. In this case, the first and second principal component explain 15 and 12 percent of the variance respectively.

5 Sample-to-sample distances

5.1 All genes, before filtering

## Obtain the sample euclidean distances
sampleDists <- stats::dist(t(assay(rld)))
sampleDistMatrix <- as.matrix(sampleDists)
## Add names based on intgroup
rownames(sampleDistMatrix) <- apply(as.data.frame(colData(rld)[, params$design]), 1,
    paste, collapse = ' : ')
colnames(sampleDistMatrix) <- NULL

## Define colors to use for the heatmap
colors <- colorRampPalette( rev(brewer.pal(9, "Blues")) )(255)

## Make the heatmap
pheatmap(sampleDistMatrix, clustering_distance_rows = sampleDists,
    clustering_distance_cols = sampleDists, color = colors)

This plot shows how samples are clustered based on their euclidean distance using the regularized log transformed count data. This figure gives an overview of how the samples are hierarchically clustered. It is a complementary figure to the PCA plot.

5.2 DEGs only

## Limit to the top n genes
# rv = rowVars(assay(rld))
# select = order(rv, decreasing = TRUE)[1:nBest]
## Obtain the sample euclidean distances
# sampleDistsTop <- dist(t(assay(rld)[select,]))
sampleDistsDEGs <- stats::dist(t(assay(rld_degs)))
# sampleDistMatrixTop <- as.matrix(sampleDistsTop)
sampleDistMatrixDEGs <- as.matrix(sampleDistsDEGs)

## Add names based on intgroup
rownames(sampleDistMatrixDEGs) <- apply(as.data.frame(colData(rld)[, params$design]), 1,
    paste, collapse = ' : ')
colnames(sampleDistMatrixDEGs) <- NULL

## Define colors to use for the heatmap
colors <- colorRampPalette( rev(brewer.pal(9, "Blues")) )(255)

## Make the heatmap
pheatmap(sampleDistMatrixDEGs,
         clustering_distance_rows = sampleDistsDEGs,
         clustering_distance_cols = sampleDistsDEGs,
         color = colors)

This plot shows how samples are clustered based on their euclidean distance using the regularized log transformed count data of all DEGs. This figure gives an overview of how the samples are hierarchically clustered. It is a complementary figure to the PCA plot.

5.3 Most variable genes only

## Limit to the top n genes
## Using the select object from PCA code above...
## Obtain the sample euclidean distances
sampleDistsTop <- stats::dist(t(assay(rld)[select,]))
sampleDistMatrixTop <- as.matrix(sampleDistsTop)

## Add names based on intgroup
rownames(sampleDistMatrixTop) <- apply(as.data.frame(colData(rld)[, params$design]), 1,
    paste, collapse = ' : ')
colnames(sampleDistMatrixTop) <- NULL

## Define colors to use for the heatmap
colors <- colorRampPalette( rev(brewer.pal(9, "Blues")) )(255)

## Make the heatmap
pheatmap(sampleDistMatrixTop,
         clustering_distance_rows = sampleDistsTop,
         clustering_distance_cols = sampleDistsTop,
         color = colors)

This plot shows how samples are clustered based on their euclidean distance using the regularized log transformed count data of the top 100 most variable genes. This figure gives an overview of how the samples are hierarchically clustered. It is a complementary figure to the PCA plot.

6 Heatmaps

6.1 All DEGs


mat <- assay(rld)[row.names(assay(rld)) %in% significantResults[[biomart_filter]],]
mat <- mat - rowMeans(mat)

heatmap_df <- as.data.frame(colData(rld)[,params$intgroup])
row.names(heatmap_df) <- colData(rld)$original_names # Customize!
pheatmap(mat,
         annotation_col=heatmap_df,
         show_rownames = FALSE,
         border_color = NA,
         scale="row")

# color = inferno(10)

6.2 Top 50 differentially abundant genes

mat_top <- assay(rld)[row.names(assay(rld)) %in% allResultsOrdered_logFC_filter[1:nHeatmapDEGs,][[biomart_filter]],]
mat_top <- mat_top - rowMeans(mat_top)

genes_for_heatmap <- row.names(mat_top)
genes_for_heatmap <- data.frame(genes=row.names(mat_top))
names(genes_for_heatmap)[names(genes_for_heatmap) == "genes"] <- biomart_filter
genes_for_heatmap <- dplyr::left_join(genes_for_heatmap, id_table_entrez,
                                     by=biomart_filter) %>%
  dplyr::distinct()

pheatmap(mat_top,
         annotation_col=heatmap_df,
         labels_row=genes_for_heatmap[["external_gene_name"]],
         show_rownames = TRUE,
         border_color = NA,
         scale="row")

# color = inferno(10)

6.3 Top 50 variable genes

rv = rowVars(assay(rld))
select = order(rv, decreasing = TRUE)[1:nHeatmap]
matRV <- assay(rld)[select,]
matRV <- matRV - rowMeans(matRV)

genes_for_heatmap <- row.names(matRV)
genes_for_heatmap <- data.frame(genes=row.names(matRV))
names(genes_for_heatmap)[names(genes_for_heatmap) == "genes"] <- biomart_filter
genes_for_heatmap <- dplyr::left_join(genes_for_heatmap, id_table_entrez,
                                      by=biomart_filter) %>%
  dplyr::distinct()

pheatmap(matRV,
         #color = rev(brewer.pal(11,"RdBu")), # inferno(10),
         annotation_col=heatmap_df,
         labels_row=genes_for_heatmap[["external_gene_name"]],
         border_color = NA,
         scale = "row",
         cutree_rows = 3,
         cutree_cols = 4)

7 MA plots

This section contains three groups of MA plots (see Wikipedia) that compare the mean of the normalized counts against the log fold change. Each of the groups has a tab for each contrast. The plots show one point per feature. The points are shown in red if the feature has an adjusted p-value less than the cutoff listed in each section, that is, the statistically significant features are shown in red.

7.1 Filtered at 0.1

This group of plots shows alpha = 0.1, which is the alpha value used to determine which resulting features were significant when running the function DESeq2::results().

## MA plot with alpha used in DESeq2::results()
for (i in seq_along(resList)) {
  contrast = gsub(pattern = paste0("log2.*", DESIGN, "\ "),
                  replacement =  "",
                  x = resList[[i]]@elementMetadata[[2]][2])
  cat("###", contrast, "  \n\n")
  MA_title <- paste0('MA plot, alpha = ', metadata(resList[[i]])$alpha,', ',contrast)
  DESeq2::plotMA(resList[[i]], alpha = metadata(resList[[i]])$alpha, main = str_wrap(MA_title, width=40))
  cat('  \n\n')
}

7.1.1 male_2 vs male_0

7.1.2 male_1000 vs male_0

7.1.3 male_5000 vs male_0

## Used to have multiple tabsets for MA plots... it was too many MA plots.

## MA plot with alpha = 1/2 of the alpha used in DESeq2::results()
# for (i in 1:length(resList)) {
# contrast = gsub(pattern = "log2.*Time\ ",
#                 replacement =  "",
#                 x = resList[[i]]@elementMetadata[[2]][2])
# cat("###", contrast, " \n")
# DESeq2::plotMA(resList[[i]], alpha = metadata(resList[[i]])$alpha / 2,
#     main = paste('MA plot with alpha =', metadata(resList[[i]])$alpha / 2,',',contrast))
# cat('\n\n')
# }

## MA plot with alpha corresponding to the one that gives the nBest features
# for (i in 1:length(resList)) {
# nBest.actual <- min(nBest, nrow(head(resList[[i]], n = nBest)))
# nBest.alpha <- head( resList[[i]][order(resList[[i]]$pvalue),], n = nBest)$padj[nBest.actual]
# contrast = gsub(pattern = "log2.*Time\ ",
#                 replacement =  "",
#                 x = resList[[i]]@elementMetadata[[2]][2])
# cat("###", contrast, " \n")
# DESeq2::plotMA(resList[[i]], alpha = nBest.alpha * 1.00000000000001,
#     main = paste('MA plot for top', nBest.actual, 'features',',',contrast))
# cat('\n\n')
# }

8 P-values

8.1 Distribution of all p-values

## P-value histogram plot

ggplot(allResults, aes(x = pvalue)) +
    geom_histogram(alpha=.5, position='identity', bins = 50) +
    labs(title='Histogram of unadjusted p-values') +
    xlab('Unadjusted p-values') +
    facet_wrap( ~ contrast, ncol = 2)

This plot shows a histogram of the unadjusted p-values. It might be skewed right or left, or flat as shown in the Wikipedia examples. The shape depends on the percent of features that are differentially expressed. For further information on how to interpret a histogram of p-values check David Robinson’s post on this topic.

## P-value distribution summary
summary(allResults$pvalue)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.0000  0.1402  0.3960  0.4256  0.6878  1.0000

This is the numerical summary of the distribution of the p-values.

## Split features by different p-value cutoffs
pval_table <- lapply(c(1e-04, 0.001, 0.01, 0.025, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5,
    0.6, 0.7, 0.8, 0.9, 1), function(x) {
    data.frame('Cut' = x, 'Count' = sum(allResults$pvalue <= x, na.rm = TRUE))
})
pval_table <- do.call(rbind, pval_table)
kable(pval_table, format = 'markdown', align = c('c', 'c'))
Cut Count
0.0001 203
0.0010 534
0.0100 1646
0.0250 2808
0.0500 4247
0.1000 6574
0.2000 10282
0.3000 13438
0.4000 16451
0.5000 19341
0.6000 22069
0.7000 24802
0.8000 27422
0.9000 30035
1.0000 32663

This table shows the number of features with p-values less or equal than some commonly used cutoff values.

8.2 Distribution of adjusted p-values

## Adjusted p-values histogram plot
ggplot(allResults, aes(x = padj)) +
    geom_histogram(alpha=.5, position='identity', bins = 50) +
    labs(title=paste('Histogram of', elementMetadata(resList[[1]])$description[grep('adjusted', elementMetadata(resList[[1]])$description)])) +
    xlab('Adjusted p-values') +
    xlim(c(0, 1.0005)) +
  facet_wrap( ~ contrast, ncol = 2, scales="free")
## Warning: Removed 6 rows containing missing values (geom_bar).

This plot shows a histogram of the fdr adjusted p-values. It might be skewed right or left, or flat as shown in the Wikipedia examples.

## Adjusted p-values distribution summary
summary(res.df$padj)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## 0.000e+00 8.700e-07 6.194e-04 2.505e-03 3.399e-03 2.760e-02

This is the numerical summary of the distribution of the fdr adjusted p-values.

## Split features by different adjusted p-value cutoffs
padj_table <- lapply(c(1e-04, 0.001, 0.01, 0.025, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5,
    0.6, 0.7, 0.8, 0.9, 1), function(x) {
    data.frame('Cut' = x, 'Count' = sum(res.df$padj <= x, na.rm = TRUE))
})
padj_table <- do.call(rbind, padj_table)
kable(padj_table, format = 'markdown', align = c('c', 'c'))
Cut Count
0.0001 55
0.0010 76
0.0100 119
0.0250 128
0.0500 129
0.1000 129
0.2000 129
0.3000 129
0.4000 129
0.5000 129
0.6000 129
0.7000 129
0.8000 129
0.9000 129
1.0000 129

This table shows the number of features with fdr adjusted p-values less or equal than some commonly used cutoff values.

9 Tables of top features

This table shows the significant DEGs (passing all filtering criteria) ordered by their absolute fold change. Use the search function to find your feature of interest or sort by one of the columns. You can limit to a single contrast if desired.

searchURL <- "http://www.ncbi.nlm.nih.gov/gene/?term="
## Add search url if appropriate
res.df.dt <- res.df
if(!is.null(searchURL)) {
    res.df.dt$ensembl_gene_id <- paste0('<a href="',
                             searchURL,
                             res.df.dt$ensembl_gene_id,
                             '" rel="noopener noreferrer" target="_blank">',
                             res.df.dt$ensembl_gene_id,
                             '<br/>',
                             res.df.dt$external_gene_name,
                             '</a>')
}

res.df.dt[, 'padj'] <- format(res.df.dt[, 'padj'], scientific = TRUE, digits=digits)
res.df.dt[, 'pvalue'] <- format(res.df.dt[, 'pvalue'], scientific = TRUE, digits=digits)
res.df.dt <- res.df.dt %>% dplyr::select(-c(description, external_gene_name))

DT::datatable(res.df.dt,
          options = list(pagingType='full_numbers',
                         pageLength=20,
                         scrollX='100%',
                         dom = 'Bfrtip',
                         buttons = c('copy',
                                     'csv',
                                     'excel',
                                     'pdf',
                                     'print',
                                     'colvis'),
                         columnDefs = list(list(visible=FALSE, targets=c(2,4,5)))),
          escape = FALSE,
          extensions = 'Buttons',
          rownames = FALSE,
          filter = "top",
          colnames = c('Gene' = 'ensembl_gene_id')) %>% 
  DT::formatRound(which(!colnames(res.df.dt) %in% c('pvalue',
                                                    'padj',
                                                    'Feature',
                                                    'contrast',
                                                    'description',
                                                    'ensembl_gene_id',
                                                    'external_gene_name')),
                  digits)

10 Gene-level plots for top 20 features

This section contains plots showing the normalized counts per sample for each group of interest. Only the best 20 features are shown, ranked by their absolute fold change values. The Y axis is on the log10 scale and the feature name is shown in the title of each plot.

plotCounts_gg <- function(i, dds, intgroup) {
    plotdata <- plotCounts(dds,
                       gene=i,
                       intgroup=params$intgroup,
                       returnData = TRUE)
    plot_title <- paste(id_table$external_gene_name[id_table[[biomart_filter]] == i],
                        id_table$ensembl_gene_id[id_table[[biomart_filter]] == i])
    if(ncol(plotdata) > 2) {
      colorCol = 3
    } else {colorCol = 2}
    ggplot(plotdata, aes(x = plotdata[,2], y = plotdata[,1], color = plotdata[,colorCol])) + 
      geom_point(position = position_jitterdodge()) +
      ylab('Normalized count') +
      xlab('Group') +
      ggtitle(plot_title) +
      coord_trans(y = "log10") +
      theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
      labs(color = colnames(plotdata)[colorCol])
}

genesToPlot <- significantResults %>% arrange(-abs(log2FoldChange))

for(i in head(unique(genesToPlot[[biomart_filter]]), nBestFeatures)) {
  print(plotCounts_gg(i, dds = dds, intgroup = params$intgroup))
}

11 Plots of genes of interest

This section shows genes of interest sorted by those with highest fold-change within each contrast.

#numResults=20
numResults <- nBestFeatures

allResultsOrdered_logFC_filter %>%
  group_by(contrast) %>%
  top_n(numResults, wt=abs(log2FoldChange)) %>%
  ungroup() %>%
  mutate(contrast=as.factor(contrast),
         external_gene_name=reorder_within(external_gene_name,log2FoldChange, contrast)) %>%
  ggplot(aes(x=log2FoldChange,
             y=external_gene_name,
             color=contrast,
             size=-log(padj))) +
  geom_point(show.legend = TRUE) +
  facet_wrap(~contrast,
             scales="free_y",
             ncol=4,
             labeller = labeller(contrast = label_wrap_gen(10))) +
  scale_y_reordered()  +
  geom_vline(xintercept=0,
             linetype="dashed",
             color = "black",
             size=1) +
  ggtitle(paste0("Top ",
                 numResults,
                 " genes ranked by fold change (adjusted p-value <",
                 alpha,
                 "), grouped by treatment"))

12 Volcano plot

This section shows a volcano plot for each contrast.

Note that scales are set manually for this plot: therefore, there may be data points outside the range shown (see warnings).

Data reported as significant are shown as red points.

ggplot(allResults, aes(x=log2FoldChange, y=-log10(padj))) +
  geom_point(size=0.5, alpha=0.4) +
  geom_point(data=significantResults, size=1.5, alpha=1, color="red") +
  facet_wrap(~contrast, ncol=4) + # , scales="free"
  geom_vline(xintercept=c(-1.5,1.5), color="red", alpha=1.0)+ 
  geom_hline(yintercept=-log10(0.05), color="blue", alpha=1.0) +
  scale_x_continuous(name = "log2 Fold Change", limits = c(-5,5)) + # 
  scale_y_continuous(name = "-log10 adjusted p-value", limits = c(0,6)) # 
## Warning: Removed 35 rows containing missing values (geom_point).
## Warning: Removed 25 rows containing missing values (geom_point).


###########################################################################################
######## PRODUCE INPUT FOR BMDExpress2 ####################################################
###########################################################################################
# Run this code conditional on whether there is a column matching the word dose in the metadata
if(any(grepl(x=colnames(DESeqDesign), pattern="dose", ignore.case = T))){
  doseCol <- grep(x=colnames(DESeqDesign), pattern="dose", ignore.case = T)
  # Create input file...
  lognorm.read.counts <- as.data.frame(counts(dds, normalized=TRUE))
  
  # rld normalized, size factor normalized, rounded to 4 significant figures
  bmdexpress <- fastbmd <- as.data.frame(lognorm.read.counts)
  bmdexpress <- cbind(SampleID=row.names(bmdexpress), bmdexpress, stringsAsFactors=F)
  bmdexpress <- rbind( Dose=c("#CLASS:DOSE", as.numeric(DESeqDesign[,doseCol])), bmdexpress, stringsAsFactors=F)
  
  # Create input for fastbmd also
  fastbmd <- cbind(SampleID=row.names(fastbmd), fastbmd, stringsAsFactors=F)
  # Optionally... you can add a second variable name (e.g., chemical) for fastbmd only
  # fastbmd <- rbind( Chemical=c("#CLASS:CHEMICAL",as.character(DESeqDesign[DESeqDesign$original_names %in% colnames(fastbmd),]$chemical)),
  #                  fastbmd,
  #                  stringsAsFactors=F)
  fastbmd <- rbind( Dose=c("#CLASS:DOSE",format(DESeqDesign[DESeqDesign$original_names %in% colnames(fastbmd),]$dose, scientific=F)),
                    fastbmd,
                    stringsAsFactors=F)
  
  if(!is.na(params$group_facet)) {
    write.table(bmdexpress,
                file=file.path(paths$DEG_output, paste0("bmdexpress_input_", params$group_filter, ".txt")),
                quote = F,
                sep = "\t",
                row.names = F,
                col.names = T)
    write.table(fastbmd,
                file=file.path(paths$DEG_output, paste0("fastbmd_input_", params$group_filter, ".txt")),
                quote = F,
                sep = "\t",
                row.names = F,
                col.names = T)
  } else {
    write.table(bmdexpress,
                file=file.path(paths$DEG_output, "bmdexpress_input.txt"),
                quote = F,
                sep = "\t",
                row.names = F,
                col.names = T)
    write.table(fastbmd,
                file=file.path(paths$DEG_output, "fastbmd_input.txt"),
                quote = F,
                sep = "\t",
                row.names = F,
                col.names = T)
  }
}

13 GO Enrichment Analysis

This section performs GO enrichment on all DEGs passing filters. The background set of genes is those that were identified in this sequencing experiment. The clusterProfiler package is used for this analysis.

## Remember that dds had ENSEMBL ids for the genes
# ensemblNames <- gsub("\\..*", "", rownames(dds))

ensemblDEGs <- significantResults[[biomart_filter]]
head(ensemblDEGs)
## [1] "ENSRNOG00000001990" "ENSRNOG00000017209" "ENSRNOG00000017445" "ENSRNOG00000017786" "ENSRNOG00000018198" "ENSRNOG00000020775"
DEGs <- dplyr::left_join(data.frame(genes=ensemblDEGs), id_table_entrez, by=c("genes" = biomart_filter))
head(DEGs)
##                genes external_gene_name                                                                           description entrezgene_id
## 1 ENSRNOG00000001990            Ugt2b10 UDP glucuronosyltransferase 2 family, polypeptide B10 [Source:RGD Symbol;Acc:1309989]        305264
## 2 ENSRNOG00000001990            Ugt2b10 UDP glucuronosyltransferase 2 family, polypeptide B10 [Source:RGD Symbol;Acc:1309989]        286954
## 3 ENSRNOG00000017209              Tubb3                              tubulin, beta 3 class III [Source:RGD Symbol;Acc:628595]        246118
## 4 ENSRNOG00000017445             Tubb2b                            tubulin, beta 2B class IIb [Source:RGD Symbol;Acc:1309427]        291081
## 5 ENSRNOG00000017786              Acta1                          actin, alpha 1, skeletal muscle [Source:RGD Symbol;Acc:2025]         29437
## 6 ENSRNOG00000018198              Dapk1                     death associated protein kinase 1 [Source:RGD Symbol;Acc:1311629]        306722
##   entrezgene_accession
## 1              Ugt2b10
## 2               Ugt2b1
## 3                Tubb3
## 4               Tubb2b
## 5                Acta1
## 6                Dapk1
entrezDEGs <- as.character(DEGs$entrezgene_id)
entrezDEGs <- entrezDEGs[!is.na(entrezDEGs)]

#### DEFINE INPUTS
myDEGs <- entrezDEGs
# background <- row.names(assay(dds))
background <- dplyr::left_join(data.frame(genes=row.names(assay(dds))), id_table_entrez, by=c("genes"=biomart_filter)) %>%
  dplyr::filter(!is.na(entrezgene_id)) %>%
  dplyr::pull(entrezgene_id)
background <- as.character(background)

entrezDEGs <- entrezDEGs[!is.na(entrezDEGs)]

##### TO DO: Add multiple panels for each contrast.
##### Use "all" argument to only run each once..?

## Not all genes have a p-value
table(!is.na(resList[[1]]$padj))
## 
##  TRUE 
## 10514

KeyType <- "ENTREZID" # ENSEMBL? ACCNUM? GID? ENTREZID?
# head(keys(orgdb, keytype="ENTREZID"))

# Run all at once...
# enrich_go_all <- enrichGO(gene = myDEGs,
#                          universe = background, # All genes in dataset
#                          OrgDb = orgdb,
#                          keyType = KeyType,
#                          readable=T,
#                          ont = "all",
#                          pAdjustMethod = "BH",
#                          pvalueCutoff = 0.01,
#                          qvalueCutoff = 0.05)

# clusterProfiler::dotplot(enrich_go_all, font.size=9, showCategory=10, split="ONTOLOGY")  +
#   theme(axis.text.y = element_text(angle = 0)) +
#   scale_y_discrete(labels = function(x) stringr::str_wrap(x, width = 50)) +
#   facet_wrap(~ONTOLOGY)

## Can also do individually...
## Perform enrichment analysis for Biological Process (BP)
enrich_go_bp <- enrichGO(gene = myDEGs,
                         universe = background, # All genes in dataset
                         OrgDb = orgdb,
                         keyType = KeyType,
                         readable=T,
                         ont = "BP",
                         pAdjustMethod = "BH",
                         pvalueCutoff = 0.01,
                         qvalueCutoff = 0.05)

enrich_go_mf <- enrichGO(gene = myDEGs,
                         universe = background, # All genes in dataset
                         OrgDb = orgdb,
                         keyType = KeyType,
                         readable=T,
                         ont = "MF",
                         pAdjustMethod = "BH",
                         pvalueCutoff = 0.01,
                         qvalueCutoff = 0.05)

enrich_go_cc <- enrichGO(gene = myDEGs,
                         universe = background, # All genes in dataset
                         OrgDb = orgdb,
                         keyType = KeyType,
                         ont = "CC",
                         pAdjustMethod = "BH",
                         pvalueCutoff = 0.01,
                         qvalueCutoff = 0.05)

13.1 Biological processes

if (nrow(enrich_go_bp@result %>% filter(p.adjust < 0.01 & qvalue < 0.05)) > 0) {
  plot1 <- clusterProfiler::dotplot(enrich_go_bp, font.size=9, showCategory=20)  +
    theme(axis.text.y = element_text(angle = 0)) +
    scale_y_discrete(labels = function(x) stringr::str_wrap(x, width = 30))
  
  DEGs_full <- dplyr::left_join(significantResults,
                                id_table_entrez,
                                by=biomart_filter) %>%
    distinct()
  foldChanges <- DEGs_full %>% dplyr::pull(log2FoldChange)
  names(foldChanges) <- DEGs_full %>% dplyr::pull(entrezgene_id)
  foldChanges <- foldChanges %>% sort() %>% rev()
  foldChanges <- foldChanges[!is.na(names(foldChanges))]
  
  simplified_bp <- simplify(enrich_go_bp, cutoff = 0.7, by = "p.adjust", select_fun = min)
  # Max size of gene set... useful for visualizing networks
  simplified_bp_for_network <- simplified_bp %>% DOSE::gsfilter(by="GSSize", max=400)
  # I believe there is a bug in the current version of DOSE or clusterProfiler that prevents this from working
  # Throws this error on plotting: Error in graph.data.frame(x, directed = FALSE) : the data frame should contain at least two columns
  simplified_bp_filt <- simplified_bp %>%
      filter(p.adjust < .05, qvalue < 0.05) %>%
      mutate(GeneRatio = DOSE::parse_ratio(GeneRatio)) %>%
      arrange(desc(GeneRatio))
  
  show3 <- simplified_bp_filt$Description[1:3] # Top 3
  show3 <- show3[!is.na(show3)]
  show5 <- simplified_bp_filt$Description[1:5] # Top 5
  show5 <- show5[!is.na(show5)]
  show10 <- simplified_bp_filt$Description[1:10] # Top 10
  show10 <- show10[!is.na(show10)]
  
  plot2 <- cnetplot(simplified_bp,
           foldChange=foldChanges,
           showCategory = show10,
           cex_label_gene=0.3)
  
  plot3 <- upsetplot(enrich_go_bp)
  plot4 <- heatplot(simplified_bp, foldChange=foldChanges, showCategory = show10) +
    scale_y_discrete(labels = function(x) stringr::str_trunc(x, width = 50))
  
  print(plot1)
  print(plot2)
  print(plot3)
  print(plot4)
} else { print("No significantly enriched terms using criteria selected") }

13.2 Molecular Functions

if (nrow(enrich_go_mf@result %>% filter(p.adjust < 0.01 & qvalue < 0.05)) > 0) {
  plot1 <- clusterProfiler::dotplot(enrich_go_mf, font.size=9)  +
     theme(axis.text.y = element_text(angle = 0)) +
    scale_y_discrete(labels = function(x) stringr::str_wrap(x, width = 30))
  
  simplified_mf <- simplify(enrich_go_mf, cutoff = 0.7, by = "p.adjust", select_fun = min)
  # Max size of gene set... useful for visualizing networks
  simplified_mf_for_network <- simplified_mf %>% DOSE::gsfilter(by="GSSize", max=400)
  # I believe there is a bug in the current version of DOSE or clusterProfiler that prevents this from working
  # Throws this error on plotting: Error in graph.data.frame(x, directed = FALSE) : the data frame should contain at least two columns
  simplified_mf_filt <- simplified_mf %>%
      filter(p.adjust < .05, qvalue < 0.05) %>%
      mutate(GeneRatio = DOSE::parse_ratio(GeneRatio)) %>%
      arrange(desc(GeneRatio))
  
  show3 <- simplified_mf_filt$Description[1:3] # Top 3
  show5 <- simplified_mf_filt$Description[1:5] # Top 5
  show10 <- simplified_mf_filt$Description[1:10] # Top 10
  
  plot2 <- cnetplot(simplified_mf,
           foldChange=foldChanges,
           showCategory = show10,
           cex_label_gene=0.3)
  
  enrich_go_mf_upset <- enrich_go_mf
  enrich_go_mf_upset@result$Description <- str_trunc(enrich_go_mf_upset@result$Description, width = 50)
  plot3 <- upsetplot(enrich_go_mf_upset)
  plot4 <- enrichplot::heatplot(simplified_mf, foldChange=foldChanges, showCategory = show10) +
    scale_y_discrete(labels = function(x) stringr::str_trunc(x, width = 50))
  
  print(plot1)
  print(plot2)
  print(plot3)
  print(plot4)
} else { print("No significantly enriched terms using criteria selected") }

13.3 Cellular Component

if (nrow(enrich_go_cc@result %>% filter(p.adjust < 0.01 & qvalue < 0.05)) > 0) {
  plot1 <- clusterProfiler::dotplot(enrich_go_cc, font.size=9)  +
     theme(axis.text.y = element_text(angle = 0)) +
    scale_y_discrete(labels = function(x) stringr::str_wrap(x, width = 30))
  
  simplified_cc <- simplify(enrich_go_cc, cutoff = 0.7, by = "p.adjust", select_fun = min)
  # Max size of gene set... useful for visualizing networks
  simplified_cc_for_network <- simplified_cc %>% DOSE::gsfilter(by="GSSize", max=400)
  # I believe there is a bug in the current version of DOSE or clusterProfiler that prevents this from working
  # Throws this error on plotting: Error in graph.data.frame(x, directed = FALSE) : the data frame should contain at least two columns
  simplified_cc_filt <- simplified_cc %>%
      filter(p.adjust < .05, qvalue < 0.05) %>%
      mutate(GeneRatio = DOSE::parse_ratio(GeneRatio)) %>%
      arrange(desc(GeneRatio))
  
  show3 <- simplified_cc_filt$Description[1:3] # Top 3
  show5 <- simplified_cc_filt$Description[1:5] # Top 5
  show10 <- simplified_cc_filt$Description[1:10] # Top 10
  
  plot2 <- cnetplot(simplified_cc,
           foldChange=foldChanges,
           showCategory = show10,
           cex_label_gene=0.3)
  
  enrich_go_cc_upset <- enrich_go_cc
  enrich_go_cc_upset@result$Description <- str_trunc(enrich_go_cc_upset@result$Description, width = 50)
  plot3 <- upsetplot(enrich_go_cc_upset)
  plot4 <- enrichplot::heatplot(simplified_cc, foldChange=foldChanges, showCategory = show10) +
    scale_y_discrete(labels = function(x) stringr::str_trunc(x, width = 50))
  
  print(plot1)
  print(plot2)
  print(plot3)
  print(plot4)
} else { print("No significantly enriched terms using criteria selected") }
## [1] "No significantly enriched terms using criteria selected"

14 Pathway Analysis using Enriched WikiPathways and GSEA (KEGG)

14.1 WikiPathways: Enrichment based on all DEGs

This section uses clusterProfiler to detect enriched gene sets using WikiPathways data.

entrez_IDs <- as.character(id_table_entrez$entrezgene_id)
entrezDEGs <- dplyr::left_join(significantResults,
                               id_table_entrez,
                               by=biomart_filter)
entrezDEGs <- as.vector(entrezDEGs$entrezgene_id)

Wiki <- rWikiPathways::downloadPathwayArchive(organism=species_sci, format = "gmt", destpath = paths$DEG_output)
wp2gene <- clusterProfiler::read.gmt(file.path(paths$DEG_output,Wiki))
wp2gene <- wp2gene %>% tidyr::separate(1, c("name","version","wpid","org"), "%")
wpid2gene <- wp2gene %>% dplyr::select(wpid,gene) #TERM2GENE
wpid2name <- wp2gene %>% dplyr::select(wpid,name) #TERM2NAME
wpid2gene
##       wpid      gene
## 1    WP124 108348093
## 2    WP124 100910144
## 3    WP124    396552
## 4    WP124    396551
## 5    WP124 100365112
## 6    WP124     65036
## 7    WP124    312382
## 8    WP124     24861
## 9    WP124    396527
## 10   WP124     25303
## 11   WP124     24346
## 12   WP124    154516
## 13   WP124     24565
## 14   WP124    501233
## 15   WP124    574523
## 16   WP124     29225
## 17   WP124    171118
## 18   WP124    501232
## 19   WP124    113992
## 20   WP124 100125372
## 21   WP124    301595
## 22  WP1276    289632
## 23  WP1276    396552
## 24  WP1276    396551
## 25  WP1276    289827
## 26  WP1276    286954
## 27  WP1276     24861
## 28  WP1276     83808
## 29  WP1276    396527
## 30  WP1276    154516
## 31  WP1276    286989
## 32  WP1276     24645
## 33  WP1276    363109
## 34  WP1276    574523
## 35  WP1276    113992
## 36  WP1276     83472
## 37  WP1276    305264
## 38  WP1276    679990
## 39  WP1276    301595
## 40  WP1277     64012
## 41  WP1277     64046
## 42  WP1277    363251
## 43  WP1277    360748
## 44  WP1277     25019
## 45  WP1277    363247
## 46  WP1277    309995
## 47  WP1278     29538
## 48  WP1278    313121
## 49  WP1278    246756
## 50  WP1278     81736
## 51  WP1278    299331
## 52  WP1278    311245
## 53  WP1278    286908
## 54  WP1278    309165
## 55  WP1278     24481
## 56  WP1278    360640
## 57  WP1278    309452
## 58  WP1278    309361
## 59  WP1278    116554
## 60  WP1278     81780
## 61  WP1278     84351
## 62  WP1278 103694380
## 63  WP1278    116590
## 64  WP1279    288588
## 65  WP1279    266713
## 66  WP1279    497672
## 67  WP1279    297893
## 68  WP1279     83828
## 69  WP1279     81649
## 70  WP1279    314322
## 71  WP1279    690966
## 72  WP1279    314436
## 73  WP1279     24890
## 74  WP1279    170922
## 75  WP1279    682902
## 76  WP1279    363287
## 77  WP1279     58919
## 78  WP1279    289561
## 79  WP1279    292778
## 80  WP1279    293621
## 81  WP1279    294236
## 82  WP1279    303918
## 83  WP1279     24224
## 84  WP1279    117526
## 85  WP1279     25636
## 86  WP1279     54244
## 87  WP1279     83503
## 88  WP1279    307485
## 89  WP1279     81646
## 90  WP1279     24185
## 91  WP1279    117017
## 92  WP1279    308415
## 93  WP1279     84351
## 94  WP1279    498109
## 95  WP1279    367858
## 96  WP1279     24516
## 97  WP1279    306516
## 98  WP1279     81504
## 99  WP1279     83805
## 100 WP1279    361365
## 101 WP1279    314612
## 102 WP1279     81673
## 103 WP1279    313845
## 104 WP1279     84389
## 105 WP1279     24790
## 106 WP1279     81674
## 107 WP1279 103690054
## 108 WP1279    363067
## 109 WP1279    680149
## 110 WP1279    171150
## 111 WP1279     84582
## 112 WP1279     50658
## 113 WP1279    116590
## 114 WP1279     84581
## 115 WP1279    288651
## 116 WP1279 100363500
## 117 WP1279    288533
## 118 WP1279    373541
## 119 WP1279    363481
## 120 WP1279    294018
## 121 WP1279    361580
## 122 WP1279     24400
## 123 WP1279 103695118
## 124 WP1279     81736
## 125 WP1279    294693
## 126 WP1279    310784
## 127 WP1279    170851
## 128 WP1279    171104
## 129 WP1279     84577
## 130 WP1279    309361
## 131 WP1279    291703
## 132 WP1279    363633
## 133 WP1280     24422
## 134 WP1280     24314
## 135 WP1280     29739
## 136 WP1280     83619
## 137 WP1280    117519
## 138 WP1280    313633
## 139 WP1280    494499
## 140 WP1280     25283
## 141 WP1280     24680
## 142 WP1280     24253
## 143 WP1280    116554
## 144 WP1280     24451
## 145 WP1280     54267
## 146 WP1280    288480
## 147 WP1281    294504
## 148 WP1281     24318
## 149 WP1281    305509
## 150 WP1281     24410
## 151 WP1281     24412
## 152 WP1281     24411
## 153 WP1281     24414
## 154 WP1281     29629
## 155 WP1281     29628
## 156 WP1281     24316
## 157 WP1281     29627
## 158 WP1281     29241
## 159 WP1281    362317
## 160 WP1281    361842
## 161 WP1281    116590
## 162 WP1281 100363500
## 163 WP1281     24408
## 164 WP1281     25432
## 165 WP1281     24245
## 166 WP1281     29437
## 167 WP1281     25636
## 168 WP1281     25050
## 169 WP1281    314988
## 170 WP1281    170851
## 171 WP1281     81646
## 172 WP1281     58960
## 173 WP1281    307861
## 174 WP1281     50689
## 175 WP1282    171347
## 176 WP1282     81676
## 177 WP1282    690050
## 178 WP1282    368066
## 179 WP1282     24661
## 180 WP1282     25331
## 181 WP1282    300691
## 182 WP1282     24267
## 183 WP1283    300677
## 184 WP1283    679739
## 185 WP1283    293655
## 186 WP1283     81728
## 187 WP1283    293652
## 188 WP1283    293453
## 189 WP1283    293130
## 190 WP1283 102553861
## 191 WP1283    499529
## 192 WP1283     26194
## 193 WP1283     26197
## 194 WP1283    689938
## 195 WP1283     26199
## 196 WP1283    361385
## 197 WP1283    299954
## 198 WP1283    114630
## 199 WP1283    362837
## 200 WP1283    690441
## 201 WP1283    171375
## 202 WP1283    681418
## 203 WP1283    171374
## 204 WP1283     26193
## 205 WP1283    689271
## 206 WP1283    301458
## 207 WP1283    140608
## 208 WP1283    295923
## 209 WP1283    296658
## 210 WP1283     26203
## 211 WP1283     26202
## 212 WP1283 100361468
## 213 WP1283     94271
## 214 WP1283     26204
## 215 WP1283    171528
## 216 WP1283    316632
## 217 WP1283    681024
## 218 WP1283    299643
## 219 WP1283     29754
## 220 WP1283     29478
## 221 WP1283    362440
## 222 WP1283     26201
## 223 WP1283     26200
## 224 WP1283    192241
## 225 WP1283    245958
## 226 WP1283    171082
## 227 WP1283    289218
## 228 WP1283    294964
## 229 WP1283    293991
## 230 WP1283     25488
## 231 WP1283     64539
## 232 WP1283    297990
## 233 WP1283     83615
## 234 WP1283     65262
## 235 WP1283 100363268
## 236 WP1283 100912599
## 237 WP1283    302526
## 238 WP1283    315167
## 239 WP1283    291660
## 240 WP1283    245965
## 241 WP1283    362588
## 242 WP1283    362344
## 243 WP1283    362749
## 244 WP1283    301123
## 245 WP1284    298947
## 246 WP1284     24918
## 247 WP1284     25125
## 248 WP1284     25124
## 249 WP1284     24335
## 250 WP1284     25126
## 251 WP1284     25467
## 252 WP1284     81504
## 253 WP1284     24336
## 254 WP1284     24699
## 255 WP1284     24514
## 256 WP1284     83805
## 257 WP1284    116551
## 258 WP1284    313845
## 259 WP1284    116590
## 260 WP1284     24703
## 261 WP1284    367264
## 262 WP1284     25676
## 263 WP1284    252971
## 264 WP1284     29376
## 265 WP1284    170851
## 266 WP1284    116680
## 267 WP1284     24185
## 268 WP1284     58960
## 269 WP1284     83681
## 270 WP1284     85385
## 271 WP1284     50689
## 272 WP1285     29277
## 273 WP1285    171335
## 274 WP1285     65030
## 275 WP1285    252934
## 276 WP1286    290623
## 277 WP1286    286921
## 278 WP1286    293451
## 279 WP1286     58953
## 280 WP1286    500257
## 281 WP1286    362035
## 282 WP1286    500892
## 283 WP1286    499689
## 284 WP1286    304322
## 285 WP1286    302302
## 286 WP1286    307838
## 287 WP1286     24421
## 288 WP1286    297029
## 289 WP1286     25355
## 290 WP1286    500359
## 291 WP1286     24267
## 292 WP1286    154516
## 293 WP1286     24423
## 294 WP1286    296972
## 295 WP1286     24422
## 296 WP1286    316435
## 297 WP1286     24424
## 298 WP1286     24426
## 299 WP1286    360268
## 300 WP1286    361510
## 301 WP1286    361631
## 302 WP1286    309465
## 303 WP1286    311257
## 304 WP1286    299566
## 305 WP1286     54246
## 306 WP1286    246245
## 307 WP1286    310848
## 308 WP1286    305264
## 309 WP1286     78959
## 310 WP1286    192242
## 311 WP1286     24912
## 312 WP1286    396552
## 313 WP1286    294449
## 314 WP1286    396551
## 315 WP1286    300850
## 316 WP1286    295934
## 317 WP1286    307092
## 318 WP1286     81869
## 319 WP1286    114846
## 320 WP1286     24192
## 321 WP1286    494499
## 322 WP1286    116632
## 323 WP1286    116631
## 324 WP1286     84468
## 325 WP1286    499302
## 326 WP1286    499422
## 327 WP1286    681913
## 328 WP1286 103690051
## 329 WP1286    691394
## 330 WP1286    303218
## 331 WP1286 108348061
## 332 WP1286    302489
## 333 WP1286    286954
## 334 WP1286    308190
## 335 WP1286     58981
## 336 WP1286    292155
## 337 WP1286     25458
## 338 WP1286     24404
## 339 WP1286    113992
## 340 WP1286    362228
## 341 WP1286     65030
## 342 WP1286    308511
## 343 WP1286    303669
## 344 WP1286    293779
## 345 WP1286    368066
## 346 WP1286    690050
## 347 WP1286     25429
## 348 WP1286 100910462
## 349 WP1286     64352
## 350 WP1286    288108
## 351 WP1286    498166
## 352 WP1286     25147
## 353 WP1286     25146
## 354 WP1286    114700
## 355 WP1286     25426
## 356 WP1286    295430
## 357 WP1286     25428
## 358 WP1286     81924
## 359 WP1286     25427
## 360 WP1286     24294
## 361 WP1286     84406
## 362 WP1286     24296
## 363 WP1286     24297
## 364 WP1286     24298
## 365 WP1286    308445
## 366 WP1286     24902
## 367 WP1286    361718
## 368 WP1286     24861
## 369 WP1286     25279
## 370 WP1286    314694
## 371 WP1286    353498
## 372 WP1286     25315
## 373 WP1286    171445
## 374 WP1286     26760
## 375 WP1286    574523
## 376 WP1286     29633
## 377 WP1286    363618
## 378 WP1286    116686
## 379 WP1286    300691
## 380 WP1286     83783
## 381 WP1286    140568
## 382 WP1286    684979
## 383 WP1286    301264
## 384 WP1286    154985
## 385 WP1286     29725
## 386 WP1286    246247
## 387 WP1286     29328
## 388 WP1286    246248
## 389 WP1286    286989
## 390 WP1286    291770
## 391 WP1286     29326
## 392 WP1286    246767
## 393 WP1286    316325
## 394 WP1286     81676
## 395 WP1286     25086
## 396 WP1286    310855
## 397 WP1286 100910526
## 398 WP1286    171072
## 399 WP1286     29680
## 400 WP1286     65185
## 401 WP1286    301595
## 402 WP1286    301517
## 403 WP1286    307859
## 404 WP1286     25256
## 405 WP1286    396527
## 406 WP1286     64305
## 407 WP1286    312495
## 408 WP1286     29738
## 409 WP1286    685402
## 410 WP1286    362782
## 411 WP1286 108348148
## 412 WP1286    498489
## 413 WP1286    171341
## 414 WP1286    289197
## 415 WP1286     84493
## 416 WP1286    292915
## 417 WP1287    293779
## 418 WP1287    311569
## 419 WP1288     64033
## 420 WP1288    315294
## 421 WP1288    300438
## 422 WP1288    161452
## 423 WP1288    315298
## 424 WP1288    317674
## 425 WP1288     64512
## 426 WP1288     24577
## 427 WP1288     64558
## 428 WP1288    266715
## 429 WP1288    312781
## 430 WP1288     29508
## 431 WP1288    315648
## 432 WP1288     84006
## 433 WP1288    117104
## 434 WP1288     25023
## 435 WP1288    363122
## 436 WP1288     58919
## 437 WP1288     29382
## 438 WP1288     64566
## 439 WP1288     81717
## 440 WP1288    313121
## 441 WP1288     54244
## 442 WP1288     25193
## 443 WP1288     25272
## 444 WP1288    500047
## 445 WP1288     84353
## 446 WP1288    282581
## 447 WP1288    282582
## 448 WP1288    414065
## 449 WP1288     24516
## 450 WP1288    499593
## 451 WP1288    315196
## 452 WP1288    294562
## 453 WP1288     24673
## 454 WP1288     25445
## 455 WP1288     25522
## 456 WP1288     25406
## 457 WP1288     81749
## 458 WP1288     84426
## 459 WP1288    308068
## 460 WP1288    691318
## 461 WP1288     25682
## 462 WP1288     84027
## 463 WP1288     24672
## 464 WP1288    114850
## 465 WP1288     83572
## 466 WP1288    303251
## 467 WP1288    497961
## 468 WP1288    364754
## 469 WP1288    364952
## 470 WP1288    117281
## 471 WP1288     50658
## 472 WP1288    303811
## 473 WP1288    293649
## 474 WP1288     25619
## 475 WP1288    266608
## 476 WP1288    501506
## 477 WP1288     24882
## 478 WP1288     25335
## 479 WP1288    311163
## 480 WP1288    170538
## 481 WP1288    295341
## 482 WP1288     24842
## 483 WP1288     24205
## 484 WP1288    312451
## 485 WP1288    311881
## 486 WP1288 103694903
## 487 WP1288     58822
## 488 WP1288     29134
## 489 WP1288    316527
## 490 WP1288     58868
## 491 WP1288    299147
## 492 WP1288    316526
## 493 WP1288    362102
## 494 WP1288    140584
## 495 WP1288     24680
## 496 WP1288     24881
## 497 WP1288     83721
## 498 WP1288    116466
## 499 WP1288    303181
## 500 WP1288    114487
##  [ reached 'max' / getOption("max.print") -- omitted 6052 rows ]
wpid2name
##       wpid                                         name
## 1    WP124                           Irinotecan Pathway
## 2    WP124                           Irinotecan Pathway
## 3    WP124                           Irinotecan Pathway
## 4    WP124                           Irinotecan Pathway
## 5    WP124                           Irinotecan Pathway
## 6    WP124                           Irinotecan Pathway
## 7    WP124                           Irinotecan Pathway
## 8    WP124                           Irinotecan Pathway
## 9    WP124                           Irinotecan Pathway
## 10   WP124                           Irinotecan Pathway
## 11   WP124                           Irinotecan Pathway
## 12   WP124                           Irinotecan Pathway
## 13   WP124                           Irinotecan Pathway
## 14   WP124                           Irinotecan Pathway
## 15   WP124                           Irinotecan Pathway
## 16   WP124                           Irinotecan Pathway
## 17   WP124                           Irinotecan Pathway
## 18   WP124                           Irinotecan Pathway
## 19   WP124                           Irinotecan Pathway
## 20   WP124                           Irinotecan Pathway
## 21   WP124                           Irinotecan Pathway
## 22  WP1276                              Glucuronidation
## 23  WP1276                              Glucuronidation
## 24  WP1276                              Glucuronidation
## 25  WP1276                              Glucuronidation
## 26  WP1276                              Glucuronidation
## 27  WP1276                              Glucuronidation
## 28  WP1276                              Glucuronidation
## 29  WP1276                              Glucuronidation
## 30  WP1276                              Glucuronidation
## 31  WP1276                              Glucuronidation
## 32  WP1276                              Glucuronidation
## 33  WP1276                              Glucuronidation
## 34  WP1276                              Glucuronidation
## 35  WP1276                              Glucuronidation
## 36  WP1276                              Glucuronidation
## 37  WP1276                              Glucuronidation
## 38  WP1276                              Glucuronidation
## 39  WP1276                              Glucuronidation
## 40  WP1277                   Non-homologous end joining
## 41  WP1277                   Non-homologous end joining
## 42  WP1277                   Non-homologous end joining
## 43  WP1277                   Non-homologous end joining
## 44  WP1277                   Non-homologous end joining
## 45  WP1277                   Non-homologous end joining
## 46  WP1277                   Non-homologous end joining
## 47  WP1278                           EBV LMP1 signaling
## 48  WP1278                           EBV LMP1 signaling
## 49  WP1278                           EBV LMP1 signaling
## 50  WP1278                           EBV LMP1 signaling
## 51  WP1278                           EBV LMP1 signaling
## 52  WP1278                           EBV LMP1 signaling
## 53  WP1278                           EBV LMP1 signaling
## 54  WP1278                           EBV LMP1 signaling
## 55  WP1278                           EBV LMP1 signaling
## 56  WP1278                           EBV LMP1 signaling
## 57  WP1278                           EBV LMP1 signaling
## 58  WP1278                           EBV LMP1 signaling
## 59  WP1278                           EBV LMP1 signaling
## 60  WP1278                           EBV LMP1 signaling
## 61  WP1278                           EBV LMP1 signaling
## 62  WP1278                           EBV LMP1 signaling
## 63  WP1278                           EBV LMP1 signaling
## 64  WP1279                           Estrogen signaling
## 65  WP1279                           Estrogen signaling
## 66  WP1279                           Estrogen signaling
## 67  WP1279                           Estrogen signaling
## 68  WP1279                           Estrogen signaling
## 69  WP1279                           Estrogen signaling
## 70  WP1279                           Estrogen signaling
## 71  WP1279                           Estrogen signaling
## 72  WP1279                           Estrogen signaling
## 73  WP1279                           Estrogen signaling
## 74  WP1279                           Estrogen signaling
## 75  WP1279                           Estrogen signaling
## 76  WP1279                           Estrogen signaling
## 77  WP1279                           Estrogen signaling
## 78  WP1279                           Estrogen signaling
## 79  WP1279                           Estrogen signaling
## 80  WP1279                           Estrogen signaling
## 81  WP1279                           Estrogen signaling
## 82  WP1279                           Estrogen signaling
## 83  WP1279                           Estrogen signaling
## 84  WP1279                           Estrogen signaling
## 85  WP1279                           Estrogen signaling
## 86  WP1279                           Estrogen signaling
## 87  WP1279                           Estrogen signaling
## 88  WP1279                           Estrogen signaling
## 89  WP1279                           Estrogen signaling
## 90  WP1279                           Estrogen signaling
## 91  WP1279                           Estrogen signaling
## 92  WP1279                           Estrogen signaling
## 93  WP1279                           Estrogen signaling
## 94  WP1279                           Estrogen signaling
## 95  WP1279                           Estrogen signaling
## 96  WP1279                           Estrogen signaling
## 97  WP1279                           Estrogen signaling
## 98  WP1279                           Estrogen signaling
## 99  WP1279                           Estrogen signaling
## 100 WP1279                           Estrogen signaling
## 101 WP1279                           Estrogen signaling
## 102 WP1279                           Estrogen signaling
## 103 WP1279                           Estrogen signaling
## 104 WP1279                           Estrogen signaling
## 105 WP1279                           Estrogen signaling
## 106 WP1279                           Estrogen signaling
## 107 WP1279                           Estrogen signaling
## 108 WP1279                           Estrogen signaling
## 109 WP1279                           Estrogen signaling
## 110 WP1279                           Estrogen signaling
## 111 WP1279                           Estrogen signaling
## 112 WP1279                           Estrogen signaling
## 113 WP1279                           Estrogen signaling
## 114 WP1279                           Estrogen signaling
## 115 WP1279                           Estrogen signaling
## 116 WP1279                           Estrogen signaling
## 117 WP1279                           Estrogen signaling
## 118 WP1279                           Estrogen signaling
## 119 WP1279                           Estrogen signaling
## 120 WP1279                           Estrogen signaling
## 121 WP1279                           Estrogen signaling
## 122 WP1279                           Estrogen signaling
## 123 WP1279                           Estrogen signaling
## 124 WP1279                           Estrogen signaling
## 125 WP1279                           Estrogen signaling
## 126 WP1279                           Estrogen signaling
## 127 WP1279                           Estrogen signaling
## 128 WP1279                           Estrogen signaling
## 129 WP1279                           Estrogen signaling
## 130 WP1279                           Estrogen signaling
## 131 WP1279                           Estrogen signaling
## 132 WP1279                           Estrogen signaling
## 133 WP1280                                   Keap1-Nrf2
## 134 WP1280                                   Keap1-Nrf2
## 135 WP1280                                   Keap1-Nrf2
## 136 WP1280                                   Keap1-Nrf2
## 137 WP1280                                   Keap1-Nrf2
## 138 WP1280                                   Keap1-Nrf2
## 139 WP1280                                   Keap1-Nrf2
## 140 WP1280                                   Keap1-Nrf2
## 141 WP1280                                   Keap1-Nrf2
## 142 WP1280                                   Keap1-Nrf2
## 143 WP1280                                   Keap1-Nrf2
## 144 WP1280                                   Keap1-Nrf2
## 145 WP1280                                   Keap1-Nrf2
## 146 WP1280                                   Keap1-Nrf2
## 147 WP1281      Hypothetical Network for Drug Addiction
## 148 WP1281      Hypothetical Network for Drug Addiction
## 149 WP1281      Hypothetical Network for Drug Addiction
## 150 WP1281      Hypothetical Network for Drug Addiction
## 151 WP1281      Hypothetical Network for Drug Addiction
## 152 WP1281      Hypothetical Network for Drug Addiction
## 153 WP1281      Hypothetical Network for Drug Addiction
## 154 WP1281      Hypothetical Network for Drug Addiction
## 155 WP1281      Hypothetical Network for Drug Addiction
## 156 WP1281      Hypothetical Network for Drug Addiction
## 157 WP1281      Hypothetical Network for Drug Addiction
## 158 WP1281      Hypothetical Network for Drug Addiction
## 159 WP1281      Hypothetical Network for Drug Addiction
## 160 WP1281      Hypothetical Network for Drug Addiction
## 161 WP1281      Hypothetical Network for Drug Addiction
## 162 WP1281      Hypothetical Network for Drug Addiction
## 163 WP1281      Hypothetical Network for Drug Addiction
## 164 WP1281      Hypothetical Network for Drug Addiction
## 165 WP1281      Hypothetical Network for Drug Addiction
## 166 WP1281      Hypothetical Network for Drug Addiction
## 167 WP1281      Hypothetical Network for Drug Addiction
## 168 WP1281      Hypothetical Network for Drug Addiction
## 169 WP1281      Hypothetical Network for Drug Addiction
## 170 WP1281      Hypothetical Network for Drug Addiction
## 171 WP1281      Hypothetical Network for Drug Addiction
## 172 WP1281      Hypothetical Network for Drug Addiction
## 173 WP1281      Hypothetical Network for Drug Addiction
## 174 WP1281      Hypothetical Network for Drug Addiction
## 175 WP1282                                  Methylation
## 176 WP1282                                  Methylation
## 177 WP1282                                  Methylation
## 178 WP1282                                  Methylation
## 179 WP1282                                  Methylation
## 180 WP1282                                  Methylation
## 181 WP1282                                  Methylation
## 182 WP1282                                  Methylation
## 183 WP1283                    Oxidative phosphorylation
## 184 WP1283                    Oxidative phosphorylation
## 185 WP1283                    Oxidative phosphorylation
## 186 WP1283                    Oxidative phosphorylation
## 187 WP1283                    Oxidative phosphorylation
## 188 WP1283                    Oxidative phosphorylation
## 189 WP1283                    Oxidative phosphorylation
## 190 WP1283                    Oxidative phosphorylation
## 191 WP1283                    Oxidative phosphorylation
## 192 WP1283                    Oxidative phosphorylation
## 193 WP1283                    Oxidative phosphorylation
## 194 WP1283                    Oxidative phosphorylation
## 195 WP1283                    Oxidative phosphorylation
## 196 WP1283                    Oxidative phosphorylation
## 197 WP1283                    Oxidative phosphorylation
## 198 WP1283                    Oxidative phosphorylation
## 199 WP1283                    Oxidative phosphorylation
## 200 WP1283                    Oxidative phosphorylation
## 201 WP1283                    Oxidative phosphorylation
## 202 WP1283                    Oxidative phosphorylation
## 203 WP1283                    Oxidative phosphorylation
## 204 WP1283                    Oxidative phosphorylation
## 205 WP1283                    Oxidative phosphorylation
## 206 WP1283                    Oxidative phosphorylation
## 207 WP1283                    Oxidative phosphorylation
## 208 WP1283                    Oxidative phosphorylation
## 209 WP1283                    Oxidative phosphorylation
## 210 WP1283                    Oxidative phosphorylation
## 211 WP1283                    Oxidative phosphorylation
## 212 WP1283                    Oxidative phosphorylation
## 213 WP1283                    Oxidative phosphorylation
## 214 WP1283                    Oxidative phosphorylation
## 215 WP1283                    Oxidative phosphorylation
## 216 WP1283                    Oxidative phosphorylation
## 217 WP1283                    Oxidative phosphorylation
## 218 WP1283                    Oxidative phosphorylation
## 219 WP1283                    Oxidative phosphorylation
## 220 WP1283                    Oxidative phosphorylation
## 221 WP1283                    Oxidative phosphorylation
## 222 WP1283                    Oxidative phosphorylation
## 223 WP1283                    Oxidative phosphorylation
## 224 WP1283                    Oxidative phosphorylation
## 225 WP1283                    Oxidative phosphorylation
## 226 WP1283                    Oxidative phosphorylation
## 227 WP1283                    Oxidative phosphorylation
## 228 WP1283                    Oxidative phosphorylation
## 229 WP1283                    Oxidative phosphorylation
## 230 WP1283                    Oxidative phosphorylation
## 231 WP1283                    Oxidative phosphorylation
## 232 WP1283                    Oxidative phosphorylation
## 233 WP1283                    Oxidative phosphorylation
## 234 WP1283                    Oxidative phosphorylation
## 235 WP1283                    Oxidative phosphorylation
## 236 WP1283                    Oxidative phosphorylation
## 237 WP1283                    Oxidative phosphorylation
## 238 WP1283                    Oxidative phosphorylation
## 239 WP1283                    Oxidative phosphorylation
## 240 WP1283                    Oxidative phosphorylation
## 241 WP1283                    Oxidative phosphorylation
## 242 WP1283                    Oxidative phosphorylation
## 243 WP1283                    Oxidative phosphorylation
## 244 WP1283                    Oxidative phosphorylation
## 245 WP1284                       EPO Receptor Signaling
## 246 WP1284                       EPO Receptor Signaling
## 247 WP1284                       EPO Receptor Signaling
## 248 WP1284                       EPO Receptor Signaling
## 249 WP1284                       EPO Receptor Signaling
## 250 WP1284                       EPO Receptor Signaling
## 251 WP1284                       EPO Receptor Signaling
## 252 WP1284                       EPO Receptor Signaling
## 253 WP1284                       EPO Receptor Signaling
## 254 WP1284                       EPO Receptor Signaling
## 255 WP1284                       EPO Receptor Signaling
## 256 WP1284                       EPO Receptor Signaling
## 257 WP1284                       EPO Receptor Signaling
## 258 WP1284                       EPO Receptor Signaling
## 259 WP1284                       EPO Receptor Signaling
## 260 WP1284                       EPO Receptor Signaling
## 261 WP1284                       EPO Receptor Signaling
## 262 WP1284                       EPO Receptor Signaling
## 263 WP1284                       EPO Receptor Signaling
## 264 WP1284                       EPO Receptor Signaling
## 265 WP1284                       EPO Receptor Signaling
## 266 WP1284                       EPO Receptor Signaling
## 267 WP1284                       EPO Receptor Signaling
## 268 WP1284                       EPO Receptor Signaling
## 269 WP1284                       EPO Receptor Signaling
## 270 WP1284                       EPO Receptor Signaling
## 271 WP1284                       EPO Receptor Signaling
## 272 WP1285 Arachidonate Epoxygenase   Epoxide Hydrolase
## 273 WP1285 Arachidonate Epoxygenase   Epoxide Hydrolase
## 274 WP1285 Arachidonate Epoxygenase   Epoxide Hydrolase
## 275 WP1285 Arachidonate Epoxygenase   Epoxide Hydrolase
## 276 WP1286                Metapathway biotransformation
## 277 WP1286                Metapathway biotransformation
## 278 WP1286                Metapathway biotransformation
## 279 WP1286                Metapathway biotransformation
## 280 WP1286                Metapathway biotransformation
## 281 WP1286                Metapathway biotransformation
## 282 WP1286                Metapathway biotransformation
## 283 WP1286                Metapathway biotransformation
## 284 WP1286                Metapathway biotransformation
## 285 WP1286                Metapathway biotransformation
## 286 WP1286                Metapathway biotransformation
## 287 WP1286                Metapathway biotransformation
## 288 WP1286                Metapathway biotransformation
## 289 WP1286                Metapathway biotransformation
## 290 WP1286                Metapathway biotransformation
## 291 WP1286                Metapathway biotransformation
## 292 WP1286                Metapathway biotransformation
## 293 WP1286                Metapathway biotransformation
## 294 WP1286                Metapathway biotransformation
## 295 WP1286                Metapathway biotransformation
## 296 WP1286                Metapathway biotransformation
## 297 WP1286                Metapathway biotransformation
## 298 WP1286                Metapathway biotransformation
## 299 WP1286                Metapathway biotransformation
## 300 WP1286                Metapathway biotransformation
## 301 WP1286                Metapathway biotransformation
## 302 WP1286                Metapathway biotransformation
## 303 WP1286                Metapathway biotransformation
## 304 WP1286                Metapathway biotransformation
## 305 WP1286                Metapathway biotransformation
## 306 WP1286                Metapathway biotransformation
## 307 WP1286                Metapathway biotransformation
## 308 WP1286                Metapathway biotransformation
## 309 WP1286                Metapathway biotransformation
## 310 WP1286                Metapathway biotransformation
## 311 WP1286                Metapathway biotransformation
## 312 WP1286                Metapathway biotransformation
## 313 WP1286                Metapathway biotransformation
## 314 WP1286                Metapathway biotransformation
## 315 WP1286                Metapathway biotransformation
## 316 WP1286                Metapathway biotransformation
## 317 WP1286                Metapathway biotransformation
## 318 WP1286                Metapathway biotransformation
## 319 WP1286                Metapathway biotransformation
## 320 WP1286                Metapathway biotransformation
## 321 WP1286                Metapathway biotransformation
## 322 WP1286                Metapathway biotransformation
## 323 WP1286                Metapathway biotransformation
## 324 WP1286                Metapathway biotransformation
## 325 WP1286                Metapathway biotransformation
## 326 WP1286                Metapathway biotransformation
## 327 WP1286                Metapathway biotransformation
## 328 WP1286                Metapathway biotransformation
## 329 WP1286                Metapathway biotransformation
## 330 WP1286                Metapathway biotransformation
## 331 WP1286                Metapathway biotransformation
## 332 WP1286                Metapathway biotransformation
## 333 WP1286                Metapathway biotransformation
## 334 WP1286                Metapathway biotransformation
## 335 WP1286                Metapathway biotransformation
## 336 WP1286                Metapathway biotransformation
## 337 WP1286                Metapathway biotransformation
## 338 WP1286                Metapathway biotransformation
## 339 WP1286                Metapathway biotransformation
## 340 WP1286                Metapathway biotransformation
## 341 WP1286                Metapathway biotransformation
## 342 WP1286                Metapathway biotransformation
## 343 WP1286                Metapathway biotransformation
## 344 WP1286                Metapathway biotransformation
## 345 WP1286                Metapathway biotransformation
## 346 WP1286                Metapathway biotransformation
## 347 WP1286                Metapathway biotransformation
## 348 WP1286                Metapathway biotransformation
## 349 WP1286                Metapathway biotransformation
## 350 WP1286                Metapathway biotransformation
## 351 WP1286                Metapathway biotransformation
## 352 WP1286                Metapathway biotransformation
## 353 WP1286                Metapathway biotransformation
## 354 WP1286                Metapathway biotransformation
## 355 WP1286                Metapathway biotransformation
## 356 WP1286                Metapathway biotransformation
## 357 WP1286                Metapathway biotransformation
## 358 WP1286                Metapathway biotransformation
## 359 WP1286                Metapathway biotransformation
## 360 WP1286                Metapathway biotransformation
## 361 WP1286                Metapathway biotransformation
## 362 WP1286                Metapathway biotransformation
## 363 WP1286                Metapathway biotransformation
## 364 WP1286                Metapathway biotransformation
## 365 WP1286                Metapathway biotransformation
## 366 WP1286                Metapathway biotransformation
## 367 WP1286                Metapathway biotransformation
## 368 WP1286                Metapathway biotransformation
## 369 WP1286                Metapathway biotransformation
## 370 WP1286                Metapathway biotransformation
## 371 WP1286                Metapathway biotransformation
## 372 WP1286                Metapathway biotransformation
## 373 WP1286                Metapathway biotransformation
## 374 WP1286                Metapathway biotransformation
## 375 WP1286                Metapathway biotransformation
## 376 WP1286                Metapathway biotransformation
## 377 WP1286                Metapathway biotransformation
## 378 WP1286                Metapathway biotransformation
## 379 WP1286                Metapathway biotransformation
## 380 WP1286                Metapathway biotransformation
## 381 WP1286                Metapathway biotransformation
## 382 WP1286                Metapathway biotransformation
## 383 WP1286                Metapathway biotransformation
## 384 WP1286                Metapathway biotransformation
## 385 WP1286                Metapathway biotransformation
## 386 WP1286                Metapathway biotransformation
## 387 WP1286                Metapathway biotransformation
## 388 WP1286                Metapathway biotransformation
## 389 WP1286                Metapathway biotransformation
## 390 WP1286                Metapathway biotransformation
## 391 WP1286                Metapathway biotransformation
## 392 WP1286                Metapathway biotransformation
## 393 WP1286                Metapathway biotransformation
## 394 WP1286                Metapathway biotransformation
## 395 WP1286                Metapathway biotransformation
## 396 WP1286                Metapathway biotransformation
## 397 WP1286                Metapathway biotransformation
## 398 WP1286                Metapathway biotransformation
## 399 WP1286                Metapathway biotransformation
## 400 WP1286                Metapathway biotransformation
## 401 WP1286                Metapathway biotransformation
## 402 WP1286                Metapathway biotransformation
## 403 WP1286                Metapathway biotransformation
## 404 WP1286                Metapathway biotransformation
## 405 WP1286                Metapathway biotransformation
## 406 WP1286                Metapathway biotransformation
## 407 WP1286                Metapathway biotransformation
## 408 WP1286                Metapathway biotransformation
## 409 WP1286                Metapathway biotransformation
## 410 WP1286                Metapathway biotransformation
## 411 WP1286                Metapathway biotransformation
## 412 WP1286                Metapathway biotransformation
## 413 WP1286                Metapathway biotransformation
## 414 WP1286                Metapathway biotransformation
## 415 WP1286                Metapathway biotransformation
## 416 WP1286                Metapathway biotransformation
## 417 WP1287       Amino acid conjugation of benzoic acid
## 418 WP1287       Amino acid conjugation of benzoic acid
## 419 WP1288       Wnt Signaling Pathway and Pluripotency
## 420 WP1288       Wnt Signaling Pathway and Pluripotency
## 421 WP1288       Wnt Signaling Pathway and Pluripotency
## 422 WP1288       Wnt Signaling Pathway and Pluripotency
## 423 WP1288       Wnt Signaling Pathway and Pluripotency
## 424 WP1288       Wnt Signaling Pathway and Pluripotency
## 425 WP1288       Wnt Signaling Pathway and Pluripotency
## 426 WP1288       Wnt Signaling Pathway and Pluripotency
## 427 WP1288       Wnt Signaling Pathway and Pluripotency
## 428 WP1288       Wnt Signaling Pathway and Pluripotency
## 429 WP1288       Wnt Signaling Pathway and Pluripotency
## 430 WP1288       Wnt Signaling Pathway and Pluripotency
## 431 WP1288       Wnt Signaling Pathway and Pluripotency
## 432 WP1288       Wnt Signaling Pathway and Pluripotency
## 433 WP1288       Wnt Signaling Pathway and Pluripotency
## 434 WP1288       Wnt Signaling Pathway and Pluripotency
## 435 WP1288       Wnt Signaling Pathway and Pluripotency
## 436 WP1288       Wnt Signaling Pathway and Pluripotency
## 437 WP1288       Wnt Signaling Pathway and Pluripotency
## 438 WP1288       Wnt Signaling Pathway and Pluripotency
## 439 WP1288       Wnt Signaling Pathway and Pluripotency
## 440 WP1288       Wnt Signaling Pathway and Pluripotency
## 441 WP1288       Wnt Signaling Pathway and Pluripotency
## 442 WP1288       Wnt Signaling Pathway and Pluripotency
## 443 WP1288       Wnt Signaling Pathway and Pluripotency
## 444 WP1288       Wnt Signaling Pathway and Pluripotency
## 445 WP1288       Wnt Signaling Pathway and Pluripotency
## 446 WP1288       Wnt Signaling Pathway and Pluripotency
## 447 WP1288       Wnt Signaling Pathway and Pluripotency
## 448 WP1288       Wnt Signaling Pathway and Pluripotency
## 449 WP1288       Wnt Signaling Pathway and Pluripotency
## 450 WP1288       Wnt Signaling Pathway and Pluripotency
## 451 WP1288       Wnt Signaling Pathway and Pluripotency
## 452 WP1288       Wnt Signaling Pathway and Pluripotency
## 453 WP1288       Wnt Signaling Pathway and Pluripotency
## 454 WP1288       Wnt Signaling Pathway and Pluripotency
## 455 WP1288       Wnt Signaling Pathway and Pluripotency
## 456 WP1288       Wnt Signaling Pathway and Pluripotency
## 457 WP1288       Wnt Signaling Pathway and Pluripotency
## 458 WP1288       Wnt Signaling Pathway and Pluripotency
## 459 WP1288       Wnt Signaling Pathway and Pluripotency
## 460 WP1288       Wnt Signaling Pathway and Pluripotency
## 461 WP1288       Wnt Signaling Pathway and Pluripotency
## 462 WP1288       Wnt Signaling Pathway and Pluripotency
## 463 WP1288       Wnt Signaling Pathway and Pluripotency
## 464 WP1288       Wnt Signaling Pathway and Pluripotency
## 465 WP1288       Wnt Signaling Pathway and Pluripotency
## 466 WP1288       Wnt Signaling Pathway and Pluripotency
## 467 WP1288       Wnt Signaling Pathway and Pluripotency
## 468 WP1288       Wnt Signaling Pathway and Pluripotency
## 469 WP1288       Wnt Signaling Pathway and Pluripotency
## 470 WP1288       Wnt Signaling Pathway and Pluripotency
## 471 WP1288       Wnt Signaling Pathway and Pluripotency
## 472 WP1288       Wnt Signaling Pathway and Pluripotency
## 473 WP1288       Wnt Signaling Pathway and Pluripotency
## 474 WP1288       Wnt Signaling Pathway and Pluripotency
## 475 WP1288       Wnt Signaling Pathway and Pluripotency
## 476 WP1288       Wnt Signaling Pathway and Pluripotency
## 477 WP1288       Wnt Signaling Pathway and Pluripotency
## 478 WP1288       Wnt Signaling Pathway and Pluripotency
## 479 WP1288       Wnt Signaling Pathway and Pluripotency
## 480 WP1288       Wnt Signaling Pathway and Pluripotency
## 481 WP1288       Wnt Signaling Pathway and Pluripotency
## 482 WP1288       Wnt Signaling Pathway and Pluripotency
## 483 WP1288       Wnt Signaling Pathway and Pluripotency
## 484 WP1288       Wnt Signaling Pathway and Pluripotency
## 485 WP1288       Wnt Signaling Pathway and Pluripotency
## 486 WP1288       Wnt Signaling Pathway and Pluripotency
## 487 WP1288       Wnt Signaling Pathway and Pluripotency
## 488 WP1288       Wnt Signaling Pathway and Pluripotency
## 489 WP1288       Wnt Signaling Pathway and Pluripotency
## 490 WP1288       Wnt Signaling Pathway and Pluripotency
## 491 WP1288       Wnt Signaling Pathway and Pluripotency
## 492 WP1288       Wnt Signaling Pathway and Pluripotency
## 493 WP1288       Wnt Signaling Pathway and Pluripotency
## 494 WP1288       Wnt Signaling Pathway and Pluripotency
## 495 WP1288       Wnt Signaling Pathway and Pluripotency
## 496 WP1288       Wnt Signaling Pathway and Pluripotency
## 497 WP1288       Wnt Signaling Pathway and Pluripotency
## 498 WP1288       Wnt Signaling Pathway and Pluripotency
## 499 WP1288       Wnt Signaling Pathway and Pluripotency
## 500 WP1288       Wnt Signaling Pathway and Pluripotency
##  [ reached 'max' / getOption("max.print") -- omitted 6052 rows ]

DEG_pathways <- clusterProfiler::enricher(
  entrezDEGs,
  universe = entrez_IDs,
  pAdjustMethod = "fdr",
  pvalueCutoff = 0.05, #p.adjust cutoff
  TERM2GENE = wpid2gene,
  TERM2NAME = wpid2name)
DEG_pathways <- DOSE::setReadable(DEG_pathways, orgdb, keyType = "ENTREZID")

if (nrow(DEG_pathways@result %>% filter(p.adjust < 0.01 & qvalue < 0.05)) > 0) {
  clusterProfiler::dotplot(DEG_pathways, showCategory = 20, font.size=9)  +
    theme(axis.text.y = element_text(angle = 0)) +
    scale_y_discrete(labels = function(x) stringr::str_wrap(x, width = 30))
} else { print("No significantly enriched terms using criteria selected") }
## wrong orderBy parameter; set to default `orderBy = "x"`
## Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.

14.2 WikiPathways: Enrichment by DEGs within each contrast

This section uses clusterProfiler enricher() to detect enriched gene sets using WikiPathways data.

plotList <- list()
for (i in seq_along(levels(significantResults$contrast))) {
  entrezDEGs <- dplyr::left_join(significantResults %>% filter(contrast==levels(significantResults$contrast)[i]),
                                 id_table_entrez,
                                 by=biomart_filter)
  entrezDEGs <- entrezDEGs$entrezgene_id
  if (length(entrezDEGs) < 10) {
    plotList[[i]] <- NA
    next
  }
  DEG_pathways <- clusterProfiler::enricher(entrezDEGs,
                                            universe = entrez_IDs,
                                            pAdjustMethod = "fdr",
                                            pvalueCutoff = 0.05, #p.adjust cutoff
                                            TERM2GENE = wpid2gene,
                                            TERM2NAME = wpid2name)
  DEG_pathways <- DOSE::setReadable(DEG_pathways, orgdb, keyType = "ENTREZID")
  if (nrow(DEG_pathways@result %>% filter(p.adjust < 0.01 & qvalue < 0.05)) > 0) {
    plotList[[i]] <- clusterProfiler::dotplot(DEG_pathways, showCategory = 20, font.size=9)  +
      theme(axis.text.y = element_text(angle = 0)) +
      scale_y_discrete(labels = function(x) stringr::str_wrap(x, width = 30))
  } else { plotList[[i]] <- NA }
}
## wrong orderBy parameter; set to default `orderBy = "x"`
## Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
## wrong orderBy parameter; set to default `orderBy = "x"`
## Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
## wrong orderBy parameter; set to default `orderBy = "x"`
## Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
for (i in seq_along(levels(significantResults$contrast))) {
  cat("###", levels(significantResults$contrast)[i], "  \n\n")
  if(any(!is.na(plotList[[i]]))) {print(plotList[[i]])} else {print("No pathway perturbations were detected within this contrast.")}
  cat('  \n\n')
}

14.2.1 male_1000 vs male_0

14.2.2 male_2 vs male_0

14.2.3 male_5000 vs male_0

14.3 GSEA: By contrast

This section uses clusterProfiler gseKEGG() to detect enriched KEGG pathways and enrichplot gseaplot2 to plot running scores.

plotListGSEA <- list()
plotListGSEA_escore <- list()
for (i in seq_along(levels(significantResults$contrast))) {
  DEGs_full <- dplyr::left_join(significantResults %>% filter(contrast==levels(significantResults$contrast)[i]),
                                id_table_entrez,
                                by=biomart_filter)
  foldChanges <- DEGs_full %>% dplyr::pull(log2FoldChange)
  names(foldChanges) <- DEGs_full %>% dplyr::pull(entrezgene_id)
  foldChanges <- foldChanges %>% sort() %>% rev()
  foldChanges <- foldChanges[!is.na(names(foldChanges))]
  if (length(foldChanges) < 10) {
    plotListGSEA[[i]] <- NA
    plotListGSEA_escore[[i]] <- NA
    next
  }
  kk <- gseKEGG(foldChanges, keyType="ncbi-geneid", organism=kegg_organism)
  gsea_sorted <- kk %>% filter(p.adjust < 0.05) %>% arrange(desc(enrichmentScore))
  if(dim(kk@result)[1] > 0){
    plotListGSEA[[i]] <- ridgeplot(kk)
    plotListGSEA_escore[[i]] <- enrichplot::gseaplot2(gsea_sorted, geneSetID = c(seq_along(gsea_sorted$ID)))
  } else {
    plotListGSEA[[i]] <- NA
    plotListGSEA_escore[[i]] <- NA
  }
}
## preparing geneSet collections...
## GSEA analysis...
## Warning in preparePathwaysAndStats(pathways, stats, minSize, maxSize, gseaParam, : There are ties in the preranked stats (35.71% of the list).
## The order of those tied genes will be arbitrary, which may produce unexpected results.
## leading edge analysis...
## done...
## preparing geneSet collections...
## GSEA analysis...
## Warning in preparePathwaysAndStats(pathways, stats, minSize, maxSize, gseaParam, : There are ties in the preranked stats (29.17% of the list).
## The order of those tied genes will be arbitrary, which may produce unexpected results.
## leading edge analysis...
## done...
for (i in seq_along(levels(significantResults$contrast))) {
  cat("###", levels(significantResults$contrast)[i], "  \n\n")
  if(any(!is.na(plotListGSEA[[i]]))) {
    print(plotListGSEA[[i]])
  } else {
    print("No pathway perturbations were detected within this contrast.")
  }
  cat('  \n\n')
}

14.3.1 male_1000 vs male_0

## Picking joint bandwidth of 0.162

14.3.2 male_2 vs male_0

[1] “No pathway perturbations were detected within this contrast.”

14.3.3 male_5000 vs male_0

## Picking joint bandwidth of 0.101

14.4 GSEA Enrichment Score Plots: By contrast

In this section, only the pathway with the top enrichment score is shown (for simplicity). Note that it is possible to plot any pathway of interest by altering the call to gseaplot.

for (i in 1:length(levels(significantResults$contrast))) {
  cat("###", levels(significantResults$contrast)[i], "  \n\n")
  if(any(!is.na(plotListGSEA_escore[[i]]))) {
    print(plotListGSEA_escore[[i]])
  } else {
    print("No pathway perturbations were detected within this contrast.")
  }
  cat('  \n\n')
}

14.4.1 male_1000 vs male_0

14.4.2 male_2 vs male_0

[1] “No pathway perturbations were detected within this contrast.”

14.4.3 male_5000 vs male_0

15 Methods Summary

Please use this as a starting point for the bioinformatics/statistics section of any publications based on the data analyzed in this report. All of the information provided here is contained elsewhere in the report, but it is aggregated here for your convenience.

There were 40 samples for which data was collected. A count matrix containing the \(80\) samples sequenced in this experiment was imported into R for statistical analysis. After removing samples with less than \(10^{6}\) reads, 76 samples were left. Following the exclusion of other samples (which ones? Why? Please explain for your experiment, if applicable.), there were 37 samples remaining. Following the recommendations set out by the Omics Data Analysis Frameworks for Regulatory application (R-ODAF) guidelines, genes were filtered to include only those where 75% of at least one experimental group were above 1 CPM, and spurious spikes were removed in which (max - median) of counts were less than (sum of counts)/(number of replicates + 1). The samples excluded from analysis are shown in the table below. We used DESeq2 1.30.0 to test for differentially abundant genes within the RNA-Seq data. The log2FoldChange shrinkage procedure used was ashr. An alpha of 0.1 was used to extract raw results, which are reported as the Wald test p-value. To account for multiple testing, fdr adjusted p-values are reported. Cook’s cutoff was set to FALSE in this analysis. Differentially expressed genes (DEGs) were filtered using a linear fold change cutoff of 1.5 and adjusted p-value of 0.05.

16 Session Info

16.1 Date the report was generated.

## [1] "2020-12-15 12:08:04 EST"

16.2 Parameters (from the list elements for each params${whatever}) used to generate this report.

unlist(params)
run_pathway_analysis TRUE
projectdir /mnt/nfs1/mmeier/projects/Template_RNASeq
project_name Flame_retardants
species rat
design group
intgroup1 group
intgroup2 dose
flag TRUE
platform RNA-Seq
group_facet sex
exclude_samples NA
exclude_groups NA
include_only_column NA
include_only_group NA
use_cached_RData FALSE
cpus 39
group_filter male

16.3 Wallclock time spent generating the report.

## Time difference of 2.74 mins

16.4 R session information.

## ─ Session info ───────────────────────────────────────────────────────────────────────────────────────────────────────
##  setting  value                       
##  version  R version 4.0.3 (2020-10-10)
##  os       Ubuntu 20.04.1 LTS          
##  system   x86_64, linux-gnu           
##  ui       RStudio                     
##  language (EN)                        
##  collate  en_CA.UTF-8                 
##  ctype    en_CA.UTF-8                 
##  tz       America/Toronto             
##  date     2020-12-15                  
## 
## ─ Packages ───────────────────────────────────────────────────────────────────────────────────────────────────────────
##  package                * version  date       lib source        
##  affy                     1.68.0   2020-10-27 [1] Bioconductor  
##  affyio                   1.60.0   2020-10-27 [1] Bioconductor  
##  annotate                 1.68.0   2020-10-27 [1] Bioconductor  
##  AnnotationDbi          * 1.52.0   2020-10-27 [1] Bioconductor  
##  AnnotationHub          * 2.22.0   2020-10-27 [1] Bioconductor  
##  ashr                     2.2-47   2020-02-20 [1] CRAN (R 4.0.2)
##  askpass                  1.1      2019-01-13 [1] CRAN (R 4.0.2)
##  assertthat               0.2.1    2019-03-21 [1] CRAN (R 4.0.2)
##  backports                1.2.0    2020-11-02 [1] CRAN (R 4.0.3)
##  Biobase                * 2.50.0   2020-10-27 [1] Bioconductor  
##  BiocFileCache          * 1.14.0   2020-10-27 [1] Bioconductor  
##  BiocGenerics           * 0.36.0   2020-10-27 [1] Bioconductor  
##  BiocManager              1.30.10  2019-11-16 [1] CRAN (R 4.0.2)
##  BiocParallel           * 1.24.1   2020-11-06 [1] Bioconductor  
##  BiocVersion              3.12.0   2020-04-27 [1] Bioconductor  
##  biomaRt                * 2.46.0   2020-10-27 [1] Bioconductor  
##  bit                      4.0.4    2020-08-04 [1] CRAN (R 4.0.2)
##  bit64                    4.0.5    2020-08-30 [1] CRAN (R 4.0.2)
##  bitops                   1.0-6    2013-08-17 [1] CRAN (R 4.0.2)
##  blob                     1.2.1    2020-01-20 [1] CRAN (R 4.0.2)
##  broom                    0.7.2    2020-10-20 [1] CRAN (R 4.0.3)
##  cellranger               1.1.0    2016-07-27 [1] CRAN (R 4.0.2)
##  cli                      2.2.0    2020-11-20 [1] CRAN (R 4.0.3)
##  clusterProfiler        * 3.18.0   2020-10-27 [1] Bioconductor  
##  colorspace               2.0-0    2020-11-11 [1] CRAN (R 4.0.3)
##  cowplot                  1.1.0    2020-09-08 [1] CRAN (R 4.0.2)
##  crayon                   1.3.4    2017-09-16 [1] CRAN (R 4.0.2)
##  crosstalk                1.1.0.1  2020-03-13 [1] CRAN (R 4.0.2)
##  curl                     4.3      2019-12-02 [1] CRAN (R 4.0.2)
##  data.table             * 1.13.2   2020-10-19 [1] CRAN (R 4.0.2)
##  DBI                      1.1.0    2019-12-15 [1] CRAN (R 4.0.2)
##  dbplyr                 * 2.0.0    2020-11-03 [1] CRAN (R 4.0.3)
##  DelayedArray             0.16.0   2020-10-27 [1] Bioconductor  
##  DESeq2                 * 1.30.0   2020-10-27 [1] Bioconductor  
##  digest                   0.6.27   2020-10-24 [1] CRAN (R 4.0.3)
##  DO.db                    2.9      2020-10-02 [1] Bioconductor  
##  DOSE                     3.16.0   2020-10-27 [1] Bioconductor  
##  downloader               0.4      2015-07-09 [1] CRAN (R 4.0.2)
##  dplyr                  * 1.0.2    2020-08-18 [1] CRAN (R 4.0.2)
##  DT                     * 0.16     2020-10-13 [1] CRAN (R 4.0.3)
##  edgeR                  * 3.32.0   2020-10-27 [1] Bioconductor  
##  ellipsis                 0.3.1    2020-05-15 [1] CRAN (R 4.0.2)
##  enrichplot             * 1.10.1   2020-11-14 [1] Bioconductor  
##  evaluate                 0.14     2019-05-28 [1] CRAN (R 4.0.2)
##  fansi                    0.4.1    2020-01-08 [1] CRAN (R 4.0.2)
##  farver                   2.0.3    2020-01-16 [1] CRAN (R 4.0.2)
##  fastmap                  1.0.1    2019-10-08 [1] CRAN (R 4.0.2)
##  fastmatch                1.1-0    2017-01-28 [1] CRAN (R 4.0.2)
##  fgsea                    1.16.0   2020-10-27 [1] Bioconductor  
##  forcats                * 0.5.0    2020-03-01 [1] CRAN (R 4.0.2)
##  fs                       1.5.0    2020-07-31 [1] CRAN (R 4.0.2)
##  genefilter               1.72.0   2020-10-27 [1] Bioconductor  
##  geneplotter              1.68.0   2020-10-27 [1] Bioconductor  
##  generics                 0.1.0    2020-10-31 [1] CRAN (R 4.0.3)
##  GenomeInfoDb           * 1.26.1   2020-11-20 [1] Bioconductor  
##  GenomeInfoDbData         1.2.4    2020-11-30 [1] Bioconductor  
##  GenomicRanges          * 1.42.0   2020-10-27 [1] Bioconductor  
##  ggforce                  0.3.2    2020-06-23 [1] CRAN (R 4.0.2)
##  ggnewscale               0.4.4    2020-12-02 [1] CRAN (R 4.0.3)
##  ggplot2                * 3.3.2    2020-06-19 [1] CRAN (R 4.0.2)
##  ggraph                   2.0.4    2020-11-16 [1] CRAN (R 4.0.3)
##  ggrepel                  0.8.2    2020-03-08 [1] CRAN (R 4.0.2)
##  ggridges                 0.5.2    2020-01-12 [1] CRAN (R 4.0.2)
##  ggupset                  0.3.0    2020-05-05 [1] CRAN (R 4.0.3)
##  glue                     1.4.2    2020-08-27 [1] CRAN (R 4.0.2)
##  GO.db                    3.12.1   2020-11-30 [1] Bioconductor  
##  GOSemSim                 2.16.1   2020-10-29 [1] Bioconductor  
##  graphlayouts             0.7.1    2020-10-26 [1] CRAN (R 4.0.3)
##  gridExtra                2.3      2017-09-09 [1] CRAN (R 4.0.2)
##  gtable                   0.3.0    2019-03-25 [1] CRAN (R 4.0.2)
##  haven                    2.3.1    2020-06-01 [1] CRAN (R 4.0.2)
##  here                     1.0.0    2020-11-15 [1] CRAN (R 4.0.2)
##  highr                    0.8      2019-03-20 [1] CRAN (R 4.0.2)
##  hms                      0.5.3    2020-01-08 [1] CRAN (R 4.0.2)
##  htmltools                0.5.0    2020-06-16 [1] CRAN (R 4.0.2)
##  htmlwidgets              1.5.2    2020-10-03 [1] CRAN (R 4.0.2)
##  httpuv                   1.5.4    2020-06-06 [1] CRAN (R 4.0.2)
##  httr                     1.4.2    2020-07-20 [1] CRAN (R 4.0.2)
##  igraph                   1.2.6    2020-10-06 [1] CRAN (R 4.0.2)
##  interactiveDisplayBase   1.28.0   2020-10-27 [1] Bioconductor  
##  invgamma                 1.1      2017-05-07 [1] CRAN (R 4.0.2)
##  IRanges                * 2.24.0   2020-10-27 [1] Bioconductor  
##  irlba                    2.3.3    2019-02-05 [1] CRAN (R 4.0.2)
##  janeaustenr              0.1.5    2017-06-10 [1] CRAN (R 4.0.2)
##  jsonlite                 1.7.1    2020-09-07 [1] CRAN (R 4.0.2)
##  kableExtra             * 1.3.1    2020-10-22 [1] CRAN (R 4.0.2)
##  knitr                  * 1.30     2020-09-22 [1] CRAN (R 4.0.2)
##  labeling                 0.4.2    2020-10-20 [1] CRAN (R 4.0.3)
##  later                    1.1.0.1  2020-06-05 [1] CRAN (R 4.0.2)
##  lattice                * 0.20-41  2020-04-02 [1] CRAN (R 4.0.2)
##  lazyeval                 0.2.2    2019-03-15 [1] CRAN (R 4.0.2)
##  lifecycle                0.2.0    2020-03-06 [1] CRAN (R 4.0.2)
##  limma                  * 3.46.0   2020-10-27 [1] Bioconductor  
##  locfit                   1.5-9.4  2020-03-25 [1] CRAN (R 4.0.2)
##  lubridate                1.7.9.2  2020-11-13 [1] CRAN (R 4.0.3)
##  magrittr               * 2.0.1    2020-11-17 [1] CRAN (R 4.0.3)
##  MASS                     7.3-53   2020-09-09 [4] CRAN (R 4.0.2)
##  Matrix                   1.2-18   2019-11-27 [4] CRAN (R 4.0.0)
##  MatrixGenerics         * 1.2.0    2020-10-27 [1] Bioconductor  
##  matrixStats            * 0.57.0   2020-09-25 [1] CRAN (R 4.0.2)
##  memoise                  1.1.0    2017-04-21 [1] CRAN (R 4.0.2)
##  mime                     0.9      2020-02-04 [1] CRAN (R 4.0.2)
##  mixsqp                   0.3-43   2020-05-14 [1] CRAN (R 4.0.2)
##  modelr                   0.1.8    2020-05-19 [1] CRAN (R 4.0.2)
##  munsell                  0.5.0    2018-06-12 [1] CRAN (R 4.0.2)
##  openssl                  1.4.3    2020-09-18 [1] CRAN (R 4.0.2)
##  openxlsx               * 4.2.3    2020-10-27 [1] CRAN (R 4.0.2)
##  org.Rn.eg.db           * 3.12.0   2020-12-01 [1] Bioconductor  
##  pheatmap               * 1.0.12   2019-01-04 [1] CRAN (R 4.0.2)
##  pillar                   1.4.7    2020-11-20 [1] CRAN (R 4.0.3)
##  pkgconfig                2.0.3    2019-09-22 [1] CRAN (R 4.0.2)
##  plotly                 * 4.9.2.1  2020-04-04 [1] CRAN (R 4.0.2)
##  plyr                     1.8.6    2020-03-03 [1] CRAN (R 4.0.2)
##  polyclip                 1.10-0   2019-03-14 [1] CRAN (R 4.0.2)
##  preprocessCore           1.52.0   2020-10-27 [1] Bioconductor  
##  prettyunits              1.1.1    2020-01-24 [1] CRAN (R 4.0.2)
##  progress                 1.2.2    2019-05-16 [1] CRAN (R 4.0.2)
##  promises                 1.1.1    2020-06-09 [1] CRAN (R 4.0.2)
##  purrr                  * 0.3.4    2020-04-17 [1] CRAN (R 4.0.2)
##  qvalue                   2.22.0   2020-10-27 [1] Bioconductor  
##  R6                       2.5.0    2020-10-28 [1] CRAN (R 4.0.3)
##  rappdirs                 0.3.1    2016-03-28 [1] CRAN (R 4.0.2)
##  RColorBrewer           * 1.1-2    2014-12-07 [1] CRAN (R 4.0.2)
##  Rcpp                     1.0.5    2020-07-06 [1] CRAN (R 4.0.2)
##  RCurl                    1.98-1.2 2020-04-18 [1] CRAN (R 4.0.2)
##  readr                  * 1.4.0    2020-10-05 [1] CRAN (R 4.0.2)
##  readxl                   1.3.1    2019-03-13 [1] CRAN (R 4.0.2)
##  reprex                   0.3.0    2019-05-16 [1] CRAN (R 4.0.2)
##  reshape2                 1.4.4    2020-04-09 [1] CRAN (R 4.0.2)
##  rjson                    0.2.20   2018-06-08 [1] CRAN (R 4.0.2)
##  rlang                    0.4.9    2020-11-26 [1] CRAN (R 4.0.3)
##  rmarkdown                2.5      2020-10-21 [1] CRAN (R 4.0.3)
##  rprojroot                2.0.2    2020-11-15 [1] CRAN (R 4.0.2)
##  RSQLite                  2.2.1    2020-09-30 [1] CRAN (R 4.0.2)
##  rstudioapi               0.13     2020-11-12 [1] CRAN (R 4.0.3)
##  rvcheck                  0.1.8    2020-03-01 [1] CRAN (R 4.0.2)
##  rvest                    0.3.6    2020-07-25 [1] CRAN (R 4.0.2)
##  rWikiPathways          * 1.10.0   2020-10-27 [1] Bioconductor  
##  S4Vectors              * 0.28.0   2020-10-27 [1] Bioconductor  
##  scales                   1.1.1    2020-05-11 [1] CRAN (R 4.0.2)
##  scatterpie               0.1.5    2020-09-09 [1] CRAN (R 4.0.2)
##  sessioninfo            * 1.1.1    2018-11-05 [1] CRAN (R 4.0.2)
##  shadowtext               0.0.7    2019-11-06 [1] CRAN (R 4.0.3)
##  shiny                    1.5.0    2020-06-23 [1] CRAN (R 4.0.2)
##  SnowballC                0.7.0    2020-04-01 [1] CRAN (R 4.0.2)
##  SQUAREM                  2020.5   2020-10-21 [1] CRAN (R 4.0.2)
##  stringi                  1.5.3    2020-09-09 [1] CRAN (R 4.0.2)
##  stringr                * 1.4.0    2019-02-10 [1] CRAN (R 4.0.2)
##  SummarizedExperiment   * 1.20.0   2020-10-27 [1] Bioconductor  
##  survival                 3.2-7    2020-09-28 [4] CRAN (R 4.0.2)
##  tibble                 * 3.0.4    2020-10-12 [1] CRAN (R 4.0.3)
##  tidygraph                1.2.0    2020-05-12 [1] CRAN (R 4.0.2)
##  tidyr                  * 1.1.2    2020-08-27 [1] CRAN (R 4.0.2)
##  tidyselect               1.1.0    2020-05-11 [1] CRAN (R 4.0.2)
##  tidytext               * 0.2.6    2020-09-20 [1] CRAN (R 4.0.2)
##  tidyverse              * 1.3.0    2019-11-21 [1] CRAN (R 4.0.2)
##  tinytex                  0.27     2020-11-01 [1] CRAN (R 4.0.3)
##  tokenizers               0.2.1    2018-03-29 [1] CRAN (R 4.0.2)
##  truncnorm                1.0-8    2018-02-27 [1] CRAN (R 4.0.2)
##  tweenr                   1.0.1    2018-12-14 [1] CRAN (R 4.0.2)
##  vctrs                    0.3.5    2020-11-17 [1] CRAN (R 4.0.3)
##  viridis                * 0.5.1    2018-03-29 [1] CRAN (R 4.0.2)
##  viridisLite            * 0.3.0    2018-02-01 [1] CRAN (R 4.0.2)
##  vsn                    * 3.58.0   2020-10-27 [1] Bioconductor  
##  webshot                  0.5.2    2019-11-22 [1] CRAN (R 4.0.2)
##  withr                    2.3.0    2020-09-22 [1] CRAN (R 4.0.2)
##  xfun                     0.19     2020-10-30 [1] CRAN (R 4.0.3)
##  XML                      3.99-0.5 2020-07-23 [1] CRAN (R 4.0.2)
##  xml2                     1.3.2    2020-04-23 [1] CRAN (R 4.0.2)
##  xtable                   1.8-4    2019-04-21 [1] CRAN (R 4.0.2)
##  XVector                  0.30.0   2020-10-27 [1] Bioconductor  
##  yaml                     2.2.1    2020-02-01 [1] CRAN (R 4.0.2)
##  zip                      2.1.1    2020-08-27 [1] CRAN (R 4.0.2)
##  zlibbioc                 1.36.0   2020-10-27 [1] Bioconductor  
## 
## [1] /home/mmeier/R/x86_64-pc-linux-gnu-library/4.0
## [2] /usr/local/lib/R/site-library
## [3] /usr/lib/R/site-library
## [4] /usr/lib/R/library

16.5 Pandoc version used: 2.7.3.

# Save complete workspace
if(is.na(params$group_facet)) {
  save.image(file = file.path(paths$RData, "Complete_analysis.RData"))
} else {
  save.image(file = file.path(paths$RData, paste0("Complete_analysis_", params$group_filter, ".RData")))
}
# Add a flag to the start of the script with if statements in all code chunks to check if the flag is set.
# If flag is TRUE, run analysis; if flag is FALSE, SKIP analysis and continue here (just to modify plotting output).
LS0tCnRpdGxlOiAiVHJhbnNjcmlwdG9tZSBQcm9maWxpbmciCmF1dGhvcjogIk1hdHRoZXcgSi4gTWVpZXIiCnN1YnRpdGxlOiAiUk5BU2VxIEFuYWx5c2lzIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdGhlbWU6IHNwYWNlbGFiICAgICAgICAgICAjIGZsYXRseSBzcGFjZWxhYiBzYW5kc3RvbmUgY2VydWxlYW4KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKcGFyYW1zOgogIHByb2plY3RkaXI6ICFyIGhlcmU6OmhlcmUoKSAjIENhbiBoYXJkIGNvZGUgaWYgZm9yIHNvbWUgcmVhc29uIHlvdSBuZWVkIHRoYXQKICBwcm9qZWN0X25hbWU6ICJGbGFtZV9yZXRhcmRhbnRzIiAgICAgIyBDaGFuZ2UgZGVwZW5kaW5nIG9uIHlvdXIgcHJvamVjdCBuYW1lCiAgc3BlY2llczogInJhdCIgICAgICAgICAgICAgICAgIyBvbmUgb2YgaHVtYW4sIG1vdXNlLCByYXQsIGhhbXN0ZXIKICBkZXNpZ246ICJncm91cCIgICAgICAgICAgICAgIyBzaW5nbGUgZXhwZXJpbWVudGFsIGdyb3VwIG9mIGludGVyZXN0OyBlbnRyaWVzIGluIHRoaXMgY29sdW1uIG11c3QgbWF0Y2ggdGhlIGNvbnRyYXN0IG5hbWVzLgogIGludGdyb3VwOiBbImdyb3VwIiwiZG9zZSJdICAgICAgICAgICAjIGV4cGVyaW1lbnRhbCBncm91cCBvZiBpbnRlcmVzdCBwbHVzIGNvdmFyaWF0ZXM7IGNhbiBiZSBtb3JlIHRoYW4gb25lCiAgZmxhZzogVFJVRSAgICAgICAgICAgICAgICAgICMgcnVucyBhbGwgYW5hbHlzaXMgYnkgZGVmYXVsdCB3aGVuIHNldCB0byBUUlVFCiAgcGxhdGZvcm06ICJSTkEtU2VxIiAgICAgICAgICMgUk5BLVNlcSBvciBUZW1wTy1TZXEKICBncm91cF9mYWNldDogIXIgTkEgICAgICAgICAgIyBJZiB5b3UgaGF2ZSBtYW55IGRpZmZlcmVudCBleHBlcmltZW50YWwgZ3JvdXBzLCB5b3UgbWF5IHN1YnNldCB0aGUgcmVwb3J0IGJ5IHNwZWNpZnlpbmcgYSBjb2x1bW4gaW4gdGhlIG1ldGFkYXRhIHRvIGZpbHRlciBncm91cHMsIGFuZCB0aGVuIHNldHRpbmcgdGhlIGdyb3VwIG9mIGludGVyZXN0IGluIGdyb3VwX2ZpbHRlcgogIGdyb3VwX2ZpbHRlcjogIXIgTkEgICAgICAgICAgICMgV2hpY2ggZ3JvdXAgd2lsbCB0aGlzIHJlcG9ydCBiZSBkb25lIG9uPwogIGV4Y2x1ZGVfc2FtcGxlczogIXIgTkEgICMgT3B0aW9uYWxseSwgYSB2ZWN0b3Igb2Ygc2FtcGxlIG5hbWVzIHRvIGV4Y2x1ZGUgZnJvbSB0aGUgYW5hbHlzaXMKICBleGNsdWRlX2dyb3VwczogIXIgTkEgIyBPcHRpb25hbGx5LCBhIHZlY3RvciBvZiBncm91cHMgdG8gZXhjbHVkZSBmcm9tIHRoZSBhbmFseXNpcy4gQnkgZGVmYXVsdCB0aGlzIGlzIGFzc3VtZWQgdG8gYmUgaW4gdGhlIGNvbHVtbiBzcGVjaWZpZWQgYnkgcGFyYW1zJGRlc2lnbi4KICBpbmNsdWRlX29ubHlfY29sdW1uOiAhciBOQSAjIFJlc3RyaWN0IGFuYWx5c2lzIHRvIGdyb3VwKHMpIGluIHRoZSBjb2x1bW4gbGlzdGVkIGhlcmUgYmFzZWQgb24gcGFyYW1zJGluY2x1ZGVfb25seV9ncm91cC4KICBpbmNsdWRlX29ubHlfZ3JvdXA6ICFyIE5BICMgUmVzdHJpY3QgYW5hbHlzaXMgdG8gdGhpcy90aGVzZSBncm91cChzKSB3aXRoaW4gdGhlIGNvbHVtbiBsaXN0ZWQgaW4gcGFyYW1zJGluY2x1ZGVfb25seV9jb2x1bW4KICB1c2VfY2FjaGVkX1JEYXRhOiBGQUxTRSAjIElmIHBvc3NpYmxlLCBsb2FkIHRoZSBzYXZlZCBSRGF0YSBmb3IgZGRzIG9iamVjdCBhbmQgZ2VuZSBJRHMKICBjcHVzOiAzOSAjIFNldCB0byBhIGxvd2VyIG51bWJlciAoZS5nLiwgMiB0byA0KSBpZiB5b3UgYXJlbid0IHdvcmtpbmcgaW4gYSBzZXJ2ZXIgZW52aXJvbm1lbnQKICBydW5fcGF0aHdheV9hbmFseXNpczogVFJVRSAjIE9wdGlvbmFsbHkgZXhjbHVkZSB3aWtpcGF0aHdheXMgYW5hbHlzaXMgLSBmb3IgZXhhbXBsZSBpZiBubyBkYXRhIGlzIGF2YWlsYWJsZSBmb3IgeW91ciBzcGVjaWVzCi0tLQoKKioqCgojIFJldmlzaW9uIEhpc3RvcnkKCioqVGhpcyB2ZXJzaW9uKiogIAogIAoqQ3VycmVudCB2ZXJzaW9uOiAxKiAgCkRhdGUgcmVwb3J0IGdlbmVyYXRlZDogYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAgIApSZXBvcnQgcHJlcGFyZWQgZm9yOiBOYW1lICAKUHVycG9zZSBvZiByZXBvcnQ6ICAKCiogRXhlcmNpc2UgdG8gYW5hbHl6ZSBSTkEtU2VxIGRhdGEgIAoKKipQcmV2aW91cyByZXZpc2lvbnMqKiAgCgpOL0EgIAoKKioqCgojIENvZGUgc2V0dXAsIERhdGEgTG9hZGluZywgYW5kIERFU2VxMiBDb2RlCgpUaGlzIHJlcG9ydCBpcyBtZWFudCB0byBoZWxwIGV4cGxvcmUgREVTZXEyIHJlc3VsdHMgYW5kIHdhcyBnZW5lcmF0ZWQgdXNpbmcgUk1hcmtkb3duLiBUaGlzIHNlY3Rpb24gY29udGFpbnMgdGhlIGNvZGUgZm9yIHNldHRpbmcgdXAgdGhlIHJlc3Qgb2YgdGhlIHJlcG9ydC4gIAoKIyMgTG9hZCBsaWJyYXJpZXMKCmBgYHtyIGRvY1NldHVwLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIyBEZWZpbmUgcGF0aHMKcGF0aHMgPC0gbGlzdCgpCnBhdGhzJHJvb3QgPC0gcGFyYW1zJHByb2plY3RkaXIKcGF0aHMkZGF0YSA8LSBwYXN0ZTAocGF0aHMkcm9vdCwgIi9kYXRhIikKcGF0aHMkcmF3IDwtIHBhc3RlMChwYXRocyRkYXRhLCAiL3JhdyIpCnBhdGhzJG91dHB1dCA8LSBwYXN0ZTAocGF0aHMkZGF0YSwgIi9vdXRwdXQiKQpwYXRocyRwcm9jZXNzZWQgPC0gcGFzdGUwKHBhdGhzJGRhdGEsICIvcHJvY2Vzc2VkIikKcGF0aHMkbWV0YWRhdGEgPC0gcGFzdGUwKHBhdGhzJHJvb3QsICIvbWV0YWRhdGEiKQpwYXRocyRyZXBvcnRzIDwtIHBhc3RlMChwYXRocyRyb290LCAiL3JlcG9ydHMiKQppZihpcy5uYShwYXJhbXMkZ3JvdXBfZmFjZXQpKSB7CiAgcGF0aHMkREVHX291dHB1dCA8LSBwYXN0ZTAocGF0aHMkcm9vdCwgIi9ERUdfb3V0cHV0IikKfSBlbHNlIHsKICBwYXRocyRERUdfb3V0cHV0IDwtIHBhc3RlMChwYXRocyRyb290LCAiL0RFR19vdXRwdXQvZ3JvdXBfIiwgcGFyYW1zJGdyb3VwX2ZpbHRlcikKfQpwYXRocyRSRGF0YSA8LSBwYXN0ZTAocGF0aHMkREVHX291dHB1dCwgIi9SRGF0YSIpCgojIyBrbml0ckJvb3N0cmFwIGFuZCBkZXZpY2UgY2h1bmsgb3B0aW9ucwojIFNldCBzbyB0aGF0IGxvbmcgbGluZXMgaW4gUiB3aWxsIGJlIHdyYXBwZWQ6CmtuaXRyOjpvcHRzX2NodW5rJHNldChib290c3RyYXAuc2hvdy5jb2RlID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBib290c3RyYXAucGFuZWwgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgY2FjaGUgPSBGQUxTRSkKI2tuaXRyOjp0aWR5Lm9wdHM9bGlzdCh3aWR0aC5jdXRvZmY9NjApLCB0aWR5PVRSVUUsIGNyb3AgPSBOVUxMICMgVXNlIGZvciBQREZzCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gcGF0aHMkcm9vdCkKCiMgU2V0IHRoaXMgdmFyaWFibGUgdG8gRkFMU0UgaWYgeW91IG9ubHkgd2FudCB0byBydW4gdGhlIHBsb3R0aW5nIGZ1bmN0aW9ucyBieSByZWxvYWRpbmcgdGhlIGxhc3QgUkRhdGEgZmlsZS4KZmxhZz1wYXJhbXMkZmxhZyAjIFRvIGV2YWx1YXRlIGFuYWx5c2lzIGNodW5rcwppZiAoZmxhZz09RkFMU0UpIHsgbG9hZChwYXN0ZTAocGFyYW1zJHByb2plY3RkaXIsIlBhcnRpYWxfYW5hbHlzaXMuUkRhdGEiKSkgfQojIFNldCB0aGlzIHZhcmlhYmxlIHRvIFRSVUUgaWYgeW91IHdvdWxkIGxpa2UgdG8gZW1iZWQgdGhlIGZpbGVzIGRpcmVjdGx5IGludG8gdGhlIEhUTUwgZm9yIHBvcnRhYmlsaXR5LiBUaGlzIHNsb3dzIGRvd24gcGFnZSByZXNwb25zaXZlbmVzcyBkcmFzdGljYWxseSwgc2luY2UgdGhlIGZpbGVzIGFyZSBnZW5lcmFsbHkgcXVpdGUgbGFyZ2UuCmVtYmVkRmlsZXM9RkFMU0UKIyBTZSB0aGlzIHZhcmlhYmxlIHRvIGJlIFRSVUUgaWYgeW91IHdhbnQgdG8gaGF2ZSBzZXBhcmF0ZSBwbG90cyBvZiB0b3AgZ2VuZXMgYXMgZGVmaW5lZCBpbiB0aGUgUi1PREFGIHRlbXBsYXRlClJfT0RBRl9wbG90cz1GQUxTRQpwYXRod2F5X2FuYWx5c2lzIDwtIGZpbGUucGF0aChwYXRocyRyb290LCJSbWQvUGF0aHdheV9hbmFseXNpcy5SbWQiKQpgYGAKCmBgYHtyICd3YXJuX3VzZXInLCBldmFsPSFmbGFnfQptZXNzYWdlKCJJZiB0aGlzIHRleHQgaXMgdmlzaWJsZSBieSBkZWZhdWx0LCB0aGlzIHJlcG9ydCB3YXMgcHJvZHVjZWQgdG8gdGVzdCBwbG90dGluZyBmdW5jdGlvbnMgYW5kIHNob3VsZCBiZSB1c2VkIGV4Y2x1c2l2ZWx5IGZvciB0ZXN0aW5nIGFuZCBkZXZlbG9wbWVudC4iKQpgYGAKCmBgYHtyIHNldHVwLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIyMjIFJlY29yZCBzdGFydCB0aW1lCnN0YXJ0VGltZSA8LSBTeXMudGltZSgpCgojIyMjIExvYWQgTGlicmFyaWVzCiMgR2VuZXJhbCBwdXJwb3NlCiMgbGlicmFyeSgnY29uZmxpY3RlZCcpCmxpYnJhcnkoJ2tuaXRyJykKbGlicmFyeSgna2FibGVFeHRyYScpCmxpYnJhcnkoJ3RpZHl2ZXJzZScpCmxpYnJhcnkoJ21hZ3JpdHRyJykKbGlicmFyeSgnZ2dwbG90MicpCmxpYnJhcnkoJ0RUJykKbGlicmFyeSgnZGF0YS50YWJsZScpCmxpYnJhcnkoJ3BoZWF0bWFwJykKbGlicmFyeSgnbGF0dGljZScpCmxpYnJhcnkoJ3RpZHl0ZXh0JykKbGlicmFyeSgnb3Blbnhsc3gnKQpsaWJyYXJ5KCdSQ29sb3JCcmV3ZXInKQpsaWJyYXJ5KCd2aXJpZGlzJykKbGlicmFyeSgnc2Vzc2lvbmluZm8nKQpsaWJyYXJ5KCdwbG90bHknKQojbGlicmFyeSgnUk1hcmlhREInKQoKIyBCaW9pbmZvcm1hdGljcy1zcGVjaWZpYwpsaWJyYXJ5KCdERVNlcTInKQpsaWJyYXJ5KCdlZGdlUicpCmxpYnJhcnkoJ2VucmljaHBsb3QnKQpsaWJyYXJ5KCdyV2lraVBhdGh3YXlzJykKbGlicmFyeSgnQmlvY1BhcmFsbGVsJykKbGlicmFyeSgnY2x1c3RlclByb2ZpbGVyJykKbGlicmFyeSgnYmlvbWFSdCcpCmxpYnJhcnkoJ0Fubm90YXRpb25IdWInKQpsaWJyYXJ5KCd2c24nKQoKIyBMb2FkIGdlbm9tZSwgZGVwZW5kcyBvbiBzcGVjaWVzCiMgVG8gYWRkOiBaZWJyYWZpc2gsIEZvbHNvbWlhIGNhbmRpZGEsIG90aGVycz8Kc3BlY2llcyA8LSBwYXJhbXMkc3BlY2llcwppZihzcGVjaWVzPT0iaHVtYW4iKXsKICAjIEh1bWFuOgogIGxpYnJhcnkoJ29yZy5Icy5lZy5kYicpCiAgb3JnZGIgPC0gIm9yZy5Icy5lZy5kYiIKICBzcGVjaWVzX3NjaSA8LSAiSG9tbyBzYXBpZW5zIgogIGVuc2VtYmxfc3BlY2llcyA8LSAiaHNhcGllbnNfZ2VuZV9lbnNlbWJsIgogIHNwZWNpZXNfZ2VuZV9zeW1ib2wgPC0gImV4dGVybmFsX2dlbmVfbmFtZSIKICBrZWdnX29yZ2FuaXNtIDwtICJoc2EiCiAgdGVtcG9zZXFfbWFuaWZlc3QgPC0gIjE5MTExM19IdW1hbl9TMTUwMF9TdXJyb2dhdGVfMi4wX01hbmlmZXN0LmNzdiIgIyBvciAxOTEwMDQgSHVtYW4gV2hvbGUgVHJhbnNjcmlwdG9tZSAyLjAgTWFuaWZlc3QueGxzeAp9IGVsc2UgewogICAgaWYoc3BlY2llcz09Im1vdXNlIil7CiAgICAgICMgTW91c2U6CiAgICAgIGxpYnJhcnkoJ29yZy5NbS5lZy5kYicpCiAgICAgIG9yZ2RiIDwtICJvcmcuTW0uZWcuZGIiCiAgICAgIHNwZWNpZXNfc2NpIDwtICJNdXMgbXVzY3VsdXMiCiAgICAgIGVuc2VtYmxfc3BlY2llcyA8LSAibW11c2N1bHVzX2dlbmVfZW5zZW1ibCIKICAgICAgc3BlY2llc19nZW5lX3N5bWJvbCA8LSAibWdpX3N5bWJvbCIKICAgICAga2VnZ19vcmdhbmlzbSA8LSAibW11IgogICAgICB0ZW1wb3NlcV9tYW5pZmVzdCA8LSAiMTgxMTMwIE1vdXNlIFMxNTAwKyBTdXJyb2dhdGUgMS4yIE1hbmlmZXN0Lnhsc3giICMgb3IgMTkwNjAzIE1vdXNlIFdob2xlIFRyYW5zY3JpcHRvbWUgMS4xIE1hbmlmZXN0Lnhsc3gKICAgIH0KICBlbHNlIHsKICAgIGlmKHNwZWNpZXM9PSJyYXQiKXsKICAgICAgIyBSYXQ6IAogICAgICBsaWJyYXJ5KCdvcmcuUm4uZWcuZGInKQogICAgICBvcmdkYiA8LSAib3JnLlJuLmVnLmRiIgogICAgICBzcGVjaWVzX3NjaSA8LSAiUmF0dHVzIG5vcnZlZ2ljdXMiCiAgICAgIGVuc2VtYmxfc3BlY2llcyA8LSAicm5vcnZlZ2ljdXNfZ2VuZV9lbnNlbWJsIgogICAgICBzcGVjaWVzX2dlbmVfc3ltYm9sIDwtICJtZ2lfc3ltYm9sIgogICAgICBrZWdnX29yZ2FuaXNtIDwtICJybm8iCiAgICAgIHRlbXBvc2VxX21hbmlmZXN0IDwtICIxOTA4MDkgUmF0IFdob2xlIFRyYW5zY3JpcHRvbWUgMS4wIE1hbmlmZXN0Lnhsc3giCiAgICB9CiAgZWxzZSB7CiAgICBpZihzcGVjaWVzPT0iaGFtc3RlciIpewogICAgICAjIEdvbGRlbiBoYW1zdGVyOgogICAgICBPcmdEYi5NYSA8LSBxdWVyeShBbm5vdGF0aW9uSHViKCksIGMoIk9yZ0RiIiwiTWVzb2NyaWNldHVzIGF1cmF0dXMiKSlbWzFdXQogICAgICBvcmdkYiA8LSAiT3JnRGIuTWEiCiAgICAgIHNwZWNpZXNfc2NpIDwtICJNZXNvY3JpY2V0dXMgYXVyYXR1cyIKICAgICAgZW5zZW1ibF9zcGVjaWVzIDwtICJtYXVyYXR1c19nZW5lX2Vuc2VtYmwiCiAgICAgIHNwZWNpZXNfZ2VuZV9zeW1ib2wgPC0gImV4dGVybmFsX2dlbmVfbmFtZSIKICAgIH0KICBlbHNlIHsKICAgIG1lc3NhZ2UoIk5vIHNwZWNpZXMgcGlja2VkISIpCiAgfQogIH0KICB9Cn0KCiMgT3B0aW9ucwpvcHRpb25zKGphdmEucGFyYW1ldGVycyA9ICItWG14MTAwMDBtIikKdGhlbWVfc2V0KHRoZW1lX2J3KCkpCmBgYAoKVGhlIGNvZGUgYWJvdmUgKG5vdCBzaG93biBieSBkZWZhdWx0KSBsb2FkcyB0aGUgcmVsZXZhbnQgUiBwYWNrYWdlcyByZXF1aXJlZCBmb3IgYW5hbHlzaXMuCgojIyBTcGVjaWZ5IHBhcmFtZXRlcnMKCmBgYHtyIHNldHVwX2NvZGUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGNvbGxhcHNlPVRSVUV9CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgUEFSQU1FVEVSUyBUTyBTRVQgTUFOVUFMTFkgICAgICAgICAgICAgICAgICAgICAgICAgICAgCQkgCiMKIyBTZXQgZmlsZSBsb2NhdGlvbnMKaWYoIWRpci5leGlzdHMocGF0aHMkREVHX291dHB1dCkpewogIGRpci5jcmVhdGUocGF0aHMkREVHX291dHB1dCwgcmVjdXJzaXZlID0gVFJVRSkKICBkaXIuY3JlYXRlKHBhdGhzJFJEYXRhLCByZWN1cnNpdmUgPSBUUlVFKQogIH0KCiMgRklMRVMgVE8gTE9BRAojIEEuIFRhYiBkZWxpbWl0ZWQgZmlsZSB3aXRoIG1lcmdlZCBSU0VNLmdlbmVzLnJlc3VsdHMgZmlsZXM6ClNhbXBsZURhdGFGaWxlIDwtIGZpbGUucGF0aChwYXRocyRwcm9jZXNzZWQsImdlbmVzLmRhdGEudHN2IikKIyBCLiBUYWIgZGVsaW1pdGVkIHNhbXBsZSBpbmZvcm1hdGlvbiBmaWxlIHdpdGggYXQgbGVhc3QgMiBjb2x1bW5zOgogICMgMS4gc2FtcGxlIG5hbWVzIGlkZW50aWNhbCB0byB0aGUgY29sdW1uIG5hbWVzIG9mIHNhbXBsZURhdGEKICAjIDIuIGNvbXBvdW5kL2dyb3VwL3doYXRldmVyIChuZWVkcyB0byBpZGVudGlmeSB0byB3aGljaCBleHBlcmltZW50YWwgZ3JvdXAgdGhlIHNhbXBsZSBiZWxvbmdzKQpTYW1wbGVLZXlGaWxlIDwtIGZpbGUucGF0aChwYXRocyRtZXRhZGF0YSwibWV0YWRhdGEudHh0IikKIyBDLiBUYWIgZGVsaW1pdGVkIGZpbGUgb2YgY29tcGFyaXNvbnMgKGNvbnRyYXN0cykgdG8gdGVzdCAKIyBHcm91cCBvZiBpbnRlcmVzdCBpbiB0aGUgbGVmdCBjb2x1bW4sIGNvbnRyb2wgZm9yIGNvbXBhcmlzb24gaW4gdGhlIHJpZ2h0IGNvbHVtbgpDb250cmFzdHNGaWxlIDwtIGZpbGUucGF0aChwYXRocyRtZXRhZGF0YSwiY29udHJhc3RzLnR4dCIpCgojIFNwZWNpZnkgd2hpY2ggZ3JvdXBzIG5lZWQgdG8gYmUgY29tcGFyZWQgCmNvbnRyYXN0cyA8LSByZWFkLmRlbGltKENvbnRyYXN0c0ZpbGUsIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UsIHNlcD0iXHQiLCBoZWFkZXI9RkFMU0UsICBxdW90ZT0iXCIiKQpzaG9ydF9jb250cmFzdF9uYW1lcyA8LSBwYXN0ZShjb250cmFzdHMkVjEsInYuIixjb250cmFzdHMkVjIpICMgQ3VzdG9taXplIHRoZXNlIGZvciB5b3VyIGV4cGVyaW1lbnQuLi4gTXVzdCBiZSBzaG9ydCBlbm91Z2ggdG8gZml0IGFzIEV4Y2VsIHRhYiB0aXRsZXMuCkRFU0lHTiA8LSBwYXJhbXMkZGVzaWduCSMgQ29sdW1uIG5hbWUgd2hpY2ggZGVmaW5lcyB0aGUgZ3JvdXBzIHRvIGJlIGNvbXBhcmVkCmludGdyb3VwIDwtIHBhcmFtcyRpbnRncm91cCAjICJJbnRlcmVzdGluZyBncm91cHMiIC0gYW4gZXhwZXJpbWVudGFsIGdyb3VwIGFuZC9vciBjb3ZhcmlhdGVzCgojIFZhcmlvdXMgcGxvdHRpbmcgYW5kIGRpc3BsYXkgb3B0aW9ucwpuQmVzdEZlYXR1cmVzIDwtIDIwICMgVGhlIG51bWJlciBvZiBiZXN0IGZlYXR1cmVzIHRvIG1ha2UgcGxvdHMgb2YgdGhlaXIgY291bnRzCm5CZXN0IDwtIDEwMCAjIE51bWJlciBvZiBmZWF0dXJlcyB0byBpbmNsdWRlIGluIHRhYmxlIGFuZCBsaW1pdGluZyBQQ0EvY2x1c3RlcmluZyBhbmFseXNpcwpuSGVhdG1hcCA8LSA1MCAjIE51bWJlciBvZiBtb3N0IHZhcmlhYmxlIGdlbmVzIGZvciBoZWF0bWFwCm5IZWF0bWFwREVHcyA8LSA1MCAjIE51bWJlciBvZiBERUdzIGZvciBoZWF0bWFwCgojIFNldCBhbmFseXNpcyBJRC4gVGhpcyBJRCB3aWxsIGJlIHVzZWQgYXMgcHJlZml4IGZvciB0aGUgb3V0cHV0IGZpbGVzCiMgTm9ybWFsbHksIGFzIGZvbGxvd3M6IHllYXIgLSBwcm9qZWN0X25hbWUgLSBncm91cF9maWx0ZXIKaWYoaXMubmEocGFyYW1zJGdyb3VwX2ZpbHRlcikpewogIGFuYWx5c2lzSUQgPC0gcGFzdGUoZm9ybWF0KFN5cy50aW1lKCksICclWScpLCBwYXJhbXMkcHJvamVjdF9uYW1lLCBzZXA9Il8iKQp9IGVsc2UgewogIGFuYWx5c2lzSUQgPC0gcGFzdGUoZm9ybWF0KFN5cy50aW1lKCksICclWScpLCBwYXJhbXMkcHJvamVjdF9uYW1lLCBwYXJhbXMkZ3JvdXBfZmlsdGVyLCBzZXA9Il8iKQp9CiMgU3BlY2lmeSB1c2VkIHBsYXRmb3JtL3RlY2hub2xvZ3kgZm9yIGRhdGEgZ2VuZXJhdGlvbjoKUGxhdGZvcm0gPC0gcGFyYW1zJHBsYXRmb3JtICMgU2hvdWxkIGJlIG9uZSBvZiAiUk5BLVNlcSIgb3IgIlRlbXBPLVNlcSIKTk9STV9UWVBFIDwtIHBhc3RlMChhbmFseXNpc0lELCAiX0RFU2VxMl8iLCBQbGF0Zm9ybSkKaWYoUGxhdGZvcm09PSJUZW1wTy1TZXEiKXtzYW1wbGVkYXRhX3NlcCA8LSAiLCJ9IGVsc2Uge3NhbXBsZWRhdGFfc2VwIDwtICJcdCJ9CiMgTWlzYyBwYXJhbWV0ZXJzCmRpZ2l0cyA9IDIgIyBGb3Igcm91bmRpbmcgbnVtYmVycwpgYGAKClRoZSBjb2RlIGFib3ZlIChub3Qgc2hvd24gYnkgZGVmYXVsdCkgc3BlY2lmaWVzIHVzZXIgcHJlZmVyZW5jZXMgYW5kIGRhdGEgbG9jYXRpb25zLgoKIyMgTG9hZCBkYXRhCgpgYGB7ciAnbG9hZF9kYXRhJywgY29sbGFwc2U9VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXZhbD1mbGFnfQojIExvYWQgaW5wdXQgZmlsZXMKc2FtcGxlRGF0YSA8LSByZWFkLmRlbGltKFNhbXBsZURhdGFGaWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgc2VwPXNhbXBsZWRhdGFfc2VwLAogICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlcj1UUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHF1b3RlPSJcIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICByb3cubmFtZXM9MSwKICAgICAgICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzPUZBTFNFKQoKREVTZXFEZXNpZ24gPC0gcmVhZC5kZWxpbShTYW1wbGVLZXlGaWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwPSJcdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyPVRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgcXVvdGU9IlwiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICByb3cubmFtZXM9MSkgIyBQaWNrIGNvbHVtbiB0aGF0IGlzIHVzZWQgaW4gSUQ7IG1pZ2h0IGJlIG1vcmUgYXBwcm9wcmlhdGUgdG8gY2hhbmdlIHRoaXMhCkRFU2VxRGVzaWduJG9yaWdpbmFsX25hbWVzIDwtIHJvd25hbWVzKERFU2VxRGVzaWduKQpERVNlcURlc2lnbkFzUmVhZCA8LSBERVNlcURlc2lnbgoKIyBQYXJhbWV0ZXItc3BlY2lmaWVkIGV4Y2x1c2lvbnMKIyBDb25kaXRpb25hbGx5IGV4Y2x1ZGUgY29udHJhc3RzIGFuZCBtZXRhZGF0YSB0aGF0IHNob3VsZCBiZSBmaWx0ZXJlZAojIDEuIEZpbHRlciBieSBncm91cCBmaWx0ZXIgLSB1c2VmdWwgZm9yIHNlbGVjdGluZyBvbmUgdHJlYXRtZW50IGF0IGEgdGltZS4gcGFyYW1zJGdyb3VwX2ZpbHRlcgojIDIuIEZpbHRlciBtYW51YWxseSAtIHVzZWZ1bCBmb3IgcmVtb3ZpbmcgaXJyZWxldmFudCBjb250cm9sIGdyb3VwcyAoZS5nLiByZWZlcmVuY2UgUk5BKS4gcGFyYW1zJGV4Y2x1ZGVfZ3JvdXBzCiMgTm90ZSB0aGF0IHRoZXNlIHNob3VsZCBvbmx5IGJlIHJ1biBpZiB0aGUgcmVzcGVjdGl2ZSB2YXJpYWJsZXMgYXJlIHNldC4KaWYoIWlzLm5hKHBhcmFtcyRncm91cF9mYWNldCkpIHsKICBjb250cmFzdHNfdG9fZmlsdGVyIDwtIERFU2VxRGVzaWduICU+JQogIGRwbHlyOjpmaWx0ZXIoISFzeW0ocGFyYW1zJGdyb3VwX2ZhY2V0KT09cGFyYW1zJGdyb3VwX2ZpbHRlcikgJT4lCiAgcHVsbChwYXJhbXMkZGVzaWduKSAlPiUgCiAgdW5pcXVlKCkKICBjb250cmFzdHMgPC0gY29udHJhc3RzICU+JSBkcGx5cjo6ZmlsdGVyKFYxICVpbiUgY29udHJhc3RzX3RvX2ZpbHRlcikKICBERVNlcURlc2lnbiA8LSBERVNlcURlc2lnbiAlPiUgZHBseXI6OmZpbHRlcighIXN5bShwYXJhbXMkZGVzaWduKSAlaW4lICh1bmxpc3QoY29udHJhc3RzKSAlPiUgdW5pcXVlKCkpICkKfQppZihhbnkoIWlzLm5hKHBhcmFtcyRleGNsdWRlX3NhbXBsZXMpKSkgewogIERFU2VxRGVzaWduIDwtIERFU2VxRGVzaWduICU+JSBkcGx5cjo6ZmlsdGVyKCFvcmlnaW5hbF9uYW1lcyAlaW4lIHBhcmFtcyRleGNsdWRlX3NhbXBsZXMpCn0KaWYoYW55KCFpcy5uYShwYXJhbXMkZXhjbHVkZV9ncm91cHMpKSkgewogIERFU2VxRGVzaWduIDwtIERFU2VxRGVzaWduICU+JSBkcGx5cjo6ZmlsdGVyKCEoISFzeW0ocGFyYW1zJGRlc2lnbikpICVpbiUgcGFyYW1zJGV4Y2x1ZGVfZ3JvdXBzKQogIGNvbnRyYXN0c190b19maWx0ZXIgPC0gREVTZXFEZXNpZ24gJT4lIAogIGRwbHlyOjpmaWx0ZXIoISghIXN5bShwYXJhbXMkZGVzaWduKSkgJWluJSBwYXJhbXMkZXhjbHVkZV9ncm91cHMpICU+JQogIHB1bGwocGFyYW1zJGRlc2lnbikgJT4lIAogIHVuaXF1ZSgpCiAgY29udHJhc3RzIDwtIGNvbnRyYXN0cyAlPiUgZHBseXI6OmZpbHRlcihWMSAlaW4lIGNvbnRyYXN0c190b19maWx0ZXIpCn0KaWYoIWlzLm5hKHBhcmFtcyRpbmNsdWRlX29ubHlfY29sdW1uKSAmICFpcy5uYShwYXJhbXMkaW5jbHVkZV9vbmx5X2dyb3VwKSkgewogIERFU2VxRGVzaWduIDwtIERFU2VxRGVzaWduICU+JSBkcGx5cjo6ZmlsdGVyKCghIXN5bShwYXJhbXMkaW5jbHVkZV9vbmx5X2NvbHVtbikpICVpbiUgcGFyYW1zJGluY2x1ZGVfb25seV9ncm91cCkKICBsaW1pdF9jb250cmFzdHMgPC0gREVTZXFEZXNpZ24gJT4lIHB1bGwoISFzeW0ocGFyYW1zJGRlc2lnbikpICU+JSB1bmlxdWUoKSAlPiUgYXMuY2hhcmFjdGVyKCkKICBjb250cmFzdHMgPC0gY29udHJhc3RzICU+JSBkcGx5cjo6ZmlsdGVyKFYxICVpbiUgbGltaXRfY29udHJhc3RzKQp9CgojIENyZWF0ZSBkaXJlY3RvcmllcwpwbG90ZGlyIDwtIHBhc3RlKHBhdGhzJERFR19vdXRwdXQsICIvcGxvdHMvIiwgc2VwPSIiKQppZighZGlyLmV4aXN0cyhwbG90ZGlyKSkge2Rpci5jcmVhdGUocGxvdGRpciwgcmVjdXJzaXZlID0gVFJVRSl9CmJhcnBsb3QuZGlyIDwtIHBhc3RlKHBsb3RkaXIsICIvYmFycGxvdF9nZW5lcy8iLCBzZXA9IiIpCmlmKCFkaXIuZXhpc3RzKGJhcnBsb3QuZGlyKSkge2Rpci5jcmVhdGUoYmFycGxvdC5kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpfQoKI1NldCBwYXJhbWV0ZXJzIGFjY29yZGluZyB0byBwbGF0Zm9ybQppZiAoUGxhdGZvcm09PSJSTkEtU2VxIil7CiAgdGhyZXNob2xkID0gMTAwMDAwMCAjIE51bWJlciBvZiBhbGlnbmVkIHJlYWRzIHBlciBzYW1wbGUgcmVxdWlyZWQKICBNaW5Db3VudCA8LSAxCiAgYWxwaGEgPC0gcEFkalZhbHVlIDwtIDAuMDUgIyBSZWxheGVkIGZyb20gMC4wMQogIGxpbmVhcl9mY19maWx0ZXIgPSAxLjUKICBiaW9tYXJ0X2ZpbHRlcj0iZW5zZW1ibF9nZW5lX2lkIgp9IGVsc2UgaWYgKFBsYXRmb3JtPT0iVGVtcE8tU2VxIikgewogIHRocmVzaG9sZCA9IDEwMDAwMCAjIE51bWJlciBvZiBhbGlnbmVkIHJlYWRzIHBlciBzYW1wbGUgcmVxdWlyZWQKICBNaW5Db3VudDwtIDAuNQogIGFscGhhIDwtIHBBZGpWYWx1ZSA8LSAwLjA1IAogIGxpbmVhcl9mY19maWx0ZXIgPSAxLjUKICBiaW9tYXJ0X2ZpbHRlcj0iZXh0ZXJuYWxfZ2VuZV9uYW1lIgogIAogIGJpb3NweWRlciA8LSByZWFkLmRlbGltKGZpbGUucGF0aCgifi9zaGFyZWQvZGJzL2Jpb3NweWRlci8iLCB0ZW1wb3NlcV9tYW5pZmVzdCksICMgQXNzYXkgbWFuaWZlc3QuLi4KICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgIHNlcD0iXHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlcj1UUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgIHF1b3RlPSJcIiIpCiAgYmlvc3B5ZGVyX2Fubm9zIDwtIGJpb3NweWRlciAlPiUgZHBseXI6OnNlbGVjdChQUk9CRV9OQU1FLCBFTlNFTUJMX0dFTkVfSUQsIEdFTkVfU1lNQk9MKQogIHNhbXBsZURhdGEkUFJPQkVfTkFNRSA8LSByb3cubmFtZXMoc2FtcGxlRGF0YSkKICBzYW1wbGVEYXRhIDwtIHNhbXBsZURhdGEgJT4lCiAgICBtdXRhdGUoR0VORV9TWU1CT0wgPSBzdHJfcmVwbGFjZShQUk9CRV9OQU1FLCAiXy4qIiwgIiIpKSAlPiUKICAgIGRwbHlyOjpncm91cF9ieShHRU5FX1NZTUJPTCkgJT4lCiAgICBkcGx5cjo6c3VtbWFyaXNlX2lmKC5wcmVkaWNhdGUgPSBmdW5jdGlvbih4KSBpcy5udW1lcmljKHgpLAogICAgICAgICAgICAgICAgICAgICAgICAuZnVucyA9IGMoInN1bSIpKSAlPiUKICAgIHVuZ3JvdXAoKQogIHNhbXBsZURhdGEgPC0gYXMuZGF0YS5mcmFtZShzYW1wbGVEYXRhKQogIHJvdy5uYW1lcyhzYW1wbGVEYXRhKSA8LSBzYW1wbGVEYXRhJEdFTkVfU1lNQk9MCiAgc2FtcGxlRGF0YSA8LSBzYW1wbGVEYXRhWywtMV0KICAjIGJpb3NweWRlciAlPiUgc2VsZWN0KEVOU0VNQkxfR0VORV9JRCkgJT4lIGRpc3RpbmN0KCkgJT4lIGNvdW50KCkKICAjIHNhbXBsZURhdGEgPC0gc2FtcGxlRGF0YVtyb3cubmFtZXMoc2FtcGxlRGF0YSkgJWluJSBiaW9zcHlkZXIkRU5TRU1CTF9HRU5FX0lELF0gIyBJZiBhbGlnbmVkIHRvIGhnMzguLi4KfSBlbHNlIHsgCiAgcHJpbnQoIlBsYXRmb3JtL3RlY2hub2xvZ3kgbm90IHJlY29nbml6ZWQiKSAKfQoKYGBgCgpUaGUgY29kZSBhYm92ZSAobm90IHNob3duIGJ5IGRlZmF1bHQpIGxvYWRzIHVzZXItcHJvdmlkZWQgc2FtcGxlIG1ldGEgZGF0YSAoaS5lLiwgaW5mb3JtYXRpb24gYWJvdXQgeW91ciBleHBlcmltZW50LCBhbHNvIGtub3duIGFzIGNvbERhdGEsIG9yLCBjb2x1bW4gZGF0YSkuIFRoaXMgYWxzbyBpbXBvcnRzIHRoZSBjb3VudCBtYXRyaXggKGkuZS4sIGEgdGFibGUgb2Ygb2JzZXJ2ZWQgY291bnRzIGluIHdoaWNoIGVhY2ggc2FtcGxlIGlzIGEgY29sdW1uIGFuZCBnZW5lcyBhcmUgcm93cykuICAKClRoZSBleHBlcmltZW50YWwgY29tcGFyaXNvbnMgb2YgaW50ZXJlc3QgdG8gYmUgdGVzdGVkIGluIHRoaXMgcmVwb3J0IGFyZSBhcyBmb2xsb3dzOiAgCgpgYGB7ciAncHJpbnRfY29udHJhc3RzJ30Ka25pdHI6OmthYmxlKGNvbnRyYXN0cywKICAgICAgICAgICAgIHJvdy5uYW1lcyA9IEYsCiAgICAgICAgICAgICBjb2wubmFtZXMgPSBjKCJHcm91cCBvZiBpbnRlcmVzdCIsICJDb250cm9sIGZvciBjb21wYXJpc29uIiksCiAgICAgICAgICAgICBjYXB0aW9uPSJDb250cmFzdHMgcmVxdWVzdGVkIGZvciB0aGlzIGFuYWx5c2lzLiIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIsIGZ1bGxfd2lkdGggPSBGLCBwb3NpdGlvbiA9ICJsZWZ0IikgJT4lCiAgc2Nyb2xsX2JveChoZWlnaHQgPSAiNDgwcHgiKQpgYGAKCgojIyBSdW4gREVTZXEyCgpgYGB7ciBydW4tREVTZXEyLCBjb2xsYXBzZT1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBldmFsPWZsYWd9CiMjIyMjIyMjIyMKIyBERVNlcTIgIwojIyMjIyMjIyMjCnByaW50KE5PUk1fVFlQRSkgIyBOYW1lIG9mIGV4cGVyaW1lbnQKCiMgRmlyc3QgZGF0YSBjbGVhbi11cDogcmVwbGFjZSBOQSAmIHJlbW92ZSBzYW1wbGVzIHdpdGggdG90YWwgcmVhZGNvdW50IDwgdGhyZXNob2xkCmluaXRpYWxTYW1wbGVEYXRhQ291bnQgPC0gbmNvbChzYW1wbGVEYXRhKQpzYW1wbGVEYXRhWyBpcy5uYShzYW1wbGVEYXRhKSBdIDwtIDAgCnNhbXBsZURhdGEgPC0gc2FtcGxlRGF0YVssKGNvbFN1bXMoc2FtcGxlRGF0YSkgPiB0aHJlc2hvbGQpXSAjIDEgbWlsbGlvbiByZWFkcyByZXF1aXJlZCBwZXIgc2FtcGxlCmZpbHRlcmVkU2FtcGxlRGF0YUNvdW50IDwtIG5jb2woc2FtcGxlRGF0YSkKIyBTb21ldGltZXMgZXh0cmEgY2xlYW51cCBtYXkgYmUgbmVlZGVkCiMgY29sbmFtZXMoc2FtcGxlRGF0YSkgPC0gZ3N1YihwYXR0ZXJuPSJeMCIsIHJlcGxhY2VtZW50PSIiLCB4PWNvbG5hbWVzKHNhbXBsZURhdGEpKQoKc2FtcGxlc19iZWZvcmUgPC0gbnJvdyhERVNlcURlc2lnbikKCiMgU2FuaXR5IGNoZWNrOiBlYWNoIHNhbXBsZSAocm93KSBpbiB0aGUgbWV0YWRhdGEgc2hvdWxkIGhhdmUgYSBjb3JyZXNwb25kaW5nIGNvbHVtbiBpbiB0aGUgY291bnQgZGF0YQptZXRhZGF0YV9pbl9zYW1wbGVkYXRhIDwtIGFsbChyb3duYW1lcyhERVNlcURlc2lnbikgJWluJSBjb2xuYW1lcyhzYW1wbGVEYXRhKSkKIyBTYW5pdHkgY2hlY2s6IGVhY2ggY29sdW1uIGluIHRoZSBjb3VudCBkYXRhIHNob3VsZCBoYXZlIGEgY29ycmVzcG9uZGluZyBzYW1wbGUgKHJvdykgaW4gdGhlIG1ldGFkYXRhCnNhbXBsZWRhdGFfaW5fbWV0YWRhdGEgPC0gYWxsKGNvbG5hbWVzKHNhbXBsZURhdGEpICVpbiUgcm93bmFtZXMoREVTZXFEZXNpZ24pKQojIEZpbmQgc2FtcGxlcyB0aGF0IHdlcmUgcmVtb3ZlZCBiZWNhdXNlIHRoZXkgd2VyZW4ndCBpbiBtZXRhZGF0YQpyZW1vdmVkIDwtIGNvbG5hbWVzKHNhbXBsZURhdGFbd2hpY2goIWNvbG5hbWVzKHNhbXBsZURhdGEpICVpbiUgcm93bmFtZXMoREVTZXFEZXNpZ24pKV0pCiMgUmVvcmRlciB0aGUgbWV0YWRhdGEgdGFibGUgdG8gY29ycmVzcG9uZCB0byB0aGUgb3JkZXIgb2YgY29sdW1ucyBpbiB0aGUgY291bnQgZGF0YQpERVNlcURlc2lnbiA8LSBERVNlcURlc2lnbltERVNlcURlc2lnbiRvcmlnaW5hbF9uYW1lcyAlaW4lIGNvbG5hbWVzKHNhbXBsZURhdGEpLF0KREVTZXFEZXNpZ24gPC0gbmEub21pdChERVNlcURlc2lnbikKc2FtcGxlRGF0YSA8LSBzYW1wbGVEYXRhWywocm93bmFtZXMoREVTZXFEZXNpZ24pKV0Kc2FtcGxlc19hZnRlciA8LSBucm93KERFU2VxRGVzaWduKQoKaGVhZChyb3duYW1lcyhERVNlcURlc2lnbikpCmhlYWQoY29sbmFtZXMoc2FtcGxlRGF0YSkpICMgT3V0cHV0IHNob3VsZCBtYXRjaAoKaW50Z3JvdXBzIDwtIHBhcmFtcyRpbnRncm91cAojIEludGdyb3VwcyBuZWVkIHRvIGJlIGZhY3RvcnMgZm9yIERFU2VxMgpERVNlcURlc2lnbltpbnRncm91cHNdIDwtIGxhcHBseShERVNlcURlc2lnbltpbnRncm91cHNdLCBmYWN0b3IpCmlmKCFpcy5uYShwYXJhbXMkZGVzaWduKSkgewogICMgSWYgdGhlcmUgaXMgYSBkb3NlIGNvbHVtbiwgcmVvcmRlciB0aGUgZXhwZXJpbWVudGFsIGdyb3VwIChERVNJR04pIGJ5IGRvc2UuCiAgaWYoYW55KGdyZXBsKHg9Y29sbmFtZXMoREVTZXFEZXNpZ24pLCBwYXR0ZXJuPSJkb3NlIiwgaWdub3JlLmNhc2UgPSBUKSkpewogICAgZG9zZUNvbCA8LSBncmVwKHg9Y29sbmFtZXMoREVTZXFEZXNpZ24pLCBwYXR0ZXJuPSJkb3NlIiwgaWdub3JlLmNhc2UgPSBUKQogICAgZGVzaWduX2ZhY3Rvcl9yZW9yZGVyZWQgPC0gZmFjdG9yKERFU2VxRGVzaWduW1twYXJhbXMkZGVzaWduXV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPXVuaXF1ZShERVNlcURlc2lnbltbcGFyYW1zJGRlc2lnbl1dW29yZGVyKERFU2VxRGVzaWduW1tkb3NlQ29sXV0pXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZD1GQUxTRSkKICAgIERFU2VxRGVzaWduW1twYXJhbXMkZGVzaWduXV0gPC0gZGVzaWduX2ZhY3Rvcl9yZW9yZGVyZWQKICB9IGVsc2UgewogICAgREVTZXFEZXNpZ25bcGFyYW1zJGRlc2lnbl0gPC0gZmFjdG9yKERFU2VxRGVzaWduWyxwYXJhbXMkZGVzaWduXSkKICB9Cn0KCmlmIChmaWxlLmV4aXN0cyhmaWxlLnBhdGgocGF0aHMkUkRhdGEsICJkZHMuUkRhdGEiKSkgJiBpcy5uYShwYXJhbXMkZ3JvdXBfZmFjZXQpICYgcGFyYW1zJHVzZV9jYWNoZWRfUkRhdGEgPT0gVFJVRSkgewogIHByaW50KHBhc3RlKCJBbHJlYWR5IGZvdW5kIERFU2VxMiBvYmplY3QgZnJvbSBwcmV2aW91cyBydW47IGxvYWRpbmcgZnJvbSBkaXNrLiIpKQogIGxvYWQoZmlsZS5wYXRoKHBhdGhzJFJEYXRhLCJkZHMuUkRhdGEiKSkKICBpZiAoIWlkZW50aWNhbChhcy5kYXRhLmZyYW1lKHJvdW5kKGNvdW50cyhkZHMpKSksCiAgICAgICAgICAgICAgICAgcm91bmQoc2FtcGxlRGF0YSksMCkpIHsKICAgIHByaW50KCJOb3QgaWRlbnRpY2FsIikKICAgIH0KICB9IGVsc2UgewogICAgaWYoZmlsZS5leGlzdHMoZmlsZS5wYXRoKHBhdGhzJFJEYXRhLCBwYXN0ZTAoImRkc18iLCBwYXJhbXMkZ3JvdXBfZmlsdGVyLCAiLlJEYXRhIikpKSAmICFpcy5uYShwYXJhbXMkZ3JvdXBfZmFjZXQpICYgcGFyYW1zJHVzZV9jYWNoZWRfUkRhdGEgPT0gVFJVRSkgewogICAgICBsb2FkKGZpbGU9ZmlsZS5wYXRoKHBhdGhzJFJEYXRhLCBwYXN0ZTAoImRkc18iLCBwYXJhbXMkZ3JvdXBfZmlsdGVyLCAiLlJEYXRhIikpKQogICAgICB9IGVsc2UgewogICAgICBkZHMgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSByb3VuZChzYW1wbGVEYXRhKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSA9IGFzLmRhdGEuZnJhbWUoREVTZXFEZXNpZ24pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSBmb3JtdWxhKHBhc3RlKCJ+IiwgREVTSUdOKSkgKSAjIHJlcGxhY2Ugd2l0aCBpbnRncm91cHMgaWYgeW91IGhhdmUgYSBnb29kIHJlYXNvbiB0bwogICAgICBkZHMgPC0gZGRzWyxyb3duYW1lcyhERVNlcURlc2lnbildCiAgICAgIGRkcyA8LSBkZHNbcm93U3Vtcyhjb3VudHMoZGRzKSkgPiAxXQogICAgICBkZHMgPC0gREVTZXEoZGRzLCBwYXJhbGxlbCA9IFRSVUUsIEJQUEFSQU09TXVsdGljb3JlUGFyYW0ocGFyYW1zJGNwdXMpKQogICAgICBpZihpcy5uYShwYXJhbXMkZ3JvdXBfZmFjZXQpKSB7CiAgICAgICAgc2F2ZShkZHMsIGZpbGU9ZmlsZS5wYXRoKHBhdGhzJFJEYXRhLCAiZGRzLlJEYXRhIikpCiAgICAgIH0gZWxzZSB7CiAgICAgICAgc2F2ZShkZHMsIGZpbGU9ZmlsZS5wYXRoKHBhdGhzJFJEYXRhLCBwYXN0ZTAoImRkc18iLCBwYXJhbXMkZ3JvdXBfZmlsdGVyLCAiLlJEYXRhIikpKQogICAgICB9CiAgICB9Cn0KCiMgIyMjIFJFTU9WRSBpZiBkb25lIGFib3ZlIGZyb20gc2NyYXRjaAojIGtlZXAgPC0gZ3JlcChjb2xEYXRhKGRkcykkY2hlbWljYWwsIHBhdHRlcm49ImNlbGxzIiwgaW52ZXJ0PVQsIGlnbm9yZS5jYXNlID0gVCkKIyBkZHMgPC0gZGRzWyxrZWVwXQojIGRkcyA8LSBkZHNbcm93Lm5hbWVzKGRkcykgJWluJSBiaW9zcHlkZXIkRU5TRU1CTF9HRU5FX0lELF0KCiMgQW5vdGhlciBzYW5pdHkgY2hlY2sgdG8gbWFrZSBzdXJlIHRoZSBvYmplY3QgbG9va3MgY29ycmVjdApyZXN1bHRzTmFtZXMoZGRzKQpoZWFkKGNvbERhdGEoZGRzKSkKaGVhZChhc3NheShkZHMpKQpoZWFkKHJvd1JhbmdlcyhkZHMpKQpzdHIoY291bnRzKGRkcykpCgojIE1ha2UgcmVndWxhcml6ZWQgbG9nIG9iamVjdCBmb3IgbGF0ZXIgcGxvdHRpbmcKI3JsZCA8LSB0cnlDYXRjaChybG9nKGRkcyksIGVycm9yID0gZnVuY3Rpb24oZSkgeyBybG9nKGRkcywgZml0VHlwZSA9ICdtZWFuJykgfSkKIyBVc2UgdnN0IGZvciBodW5kcmVkcyBvZiBzYW1wbGVzIQpybGQgPC0gdnN0KGRkcykgIyBTaG91bGQgdGhpcyBiZSBibGluZD8KIApgYGAKClRoZSBjb2RlIGFib3ZlIChub3Qgc2hvd24gYnkgZGVmYXVsdCkgdXNlcyBERVNlcTIgYHIgcGFja2FnZVZlcnNpb24oIkRFU2VxMiIpYCB0byB0ZXN0IGZvciBkZWZlcmVudGlhbGx5IGFidW5kYW50IGdlbmVzIHdpdGhpbiB0aGUgYHIgUGxhdGZvcm1gIGRhdGEuICAKClByaW9yIHRvIHJ1bm5pbmcgREVTZXEyLCB0aGUgZGF0YSB3YXMgZmlsdGVyZWQgdG8gcmVtb3ZlIHNhbXBsZXMgdGhhdCBkbyBub3QgaGF2ZSAkYHIgdGhyZXNob2xkYCQgcmVhZHMgcGVyIHNhbXBsZS4gIAoKVGhlIHVzZXItcHJvdmlkZWQgbWV0YWRhdGEgaW5pdGlhbGx5IGluY2x1ZGVkIGByIHNhbXBsZXNfYmVmb3JlYCBzYW1wbGVzLiAgCgpUaGUgY291bnQgbWF0cml4IGluaXRpYWxseSBpbmNsdWRlZCBgciBpbml0aWFsU2FtcGxlRGF0YUNvdW50YCBzYW1wbGVzIChpbmNsdWRpbmcgYW55IHJlZmVyZW5jZSBtYXRlcmlhbCBzYW1wbGVzKS4gQWZ0ZXIgcmVtb3Zpbmcgc2FtcGxlcyB3aXRoIGxlc3MgdGhhbiAkYHIgdGhyZXNob2xkYCQgcmVhZHMsIGByIGZpbHRlcmVkU2FtcGxlRGF0YUNvdW50YCBzYW1wbGVzIHdlcmUgbGVmdC4gSXQgaXMgYHIgbWV0YWRhdGFfaW5fc2FtcGxlZGF0YWAgdGhhdCBhbGwgdGhlIHNhbXBsZXMgcHJvdmlkZWQgaW4gdGhlIG1ldGFkYXRhIHRhYmxlIHdlcmUgYWxzbyBpZGVudGlmaWVkIGluIHRoZSBjb3VudCBtYXRyaXguIEl0IGlzIGByIHNhbXBsZWRhdGFfaW5fbWV0YWRhdGFgIHRoYXQgYWxsIHRoZSBzYW1wbGVzIGluIHRoZSBjb3VudCBtYXRyaXggd2VyZSBhbHNvIGlkZW50aWZpZWQgaW4gdGhlIG1ldGFkYXRhIHRhYmxlLiAgCgpGb2xsb3dpbmcgdGhlIHJlbW92YWwgb2Ygb3RoZXIgc2FtcGxlcyAoaS5lLiwgcmVmZXJlbmNlIFJOQSwgZXRjLiksIHRoZXJlIHdlcmUgYHIgc2FtcGxlc19hZnRlcmAgc2FtcGxlcyByZW1haW5pbmcgaW4gdGhlIGV4cGVyaW1lbnQuIFRoZSBzYW1wbGVzIGV4Y2x1ZGVkIGZyb20gYW5hbHlzaXMgYXJlIHNob3duIGluIHRoZSB0YWJsZSBpbiB0aGUgc2VjdGlvbiB0aXRsZWQgIlNhbXBsZSBkYXRhIiwgYWxvbmcgd2l0aCBjb21wbGV0ZSBzYW1wbGUgbWV0YWRhdGEgZm9yIHRoZSBleHBlcmltZW50LiAgICAKCiMjIFNhbXBsZSBkYXRhIChtZXRhZGF0YSBhYm91dCB5b3VyIGV4cGVyaW1lbnQpIHsudGFic2V0IC50YWJzZXQtZmFkZX0KCiMjIyBTYW1wbGVzIHVzZWQgaW4gdGhpcyByZXBvcnQKClRoaXMgdGFibGUgc2hvd3MgdGhlIGZpbmFsIGxpc3Qgb2Ygc2FtcGxlcyB0aGF0IHdlcmUgdXNlZCBpbiB0aGUgZGF0YSBhbmFseXNpcyAoYXMgd2VsbCBhcyB0aGUgY29ycmVzcG9uZGluZyBzYW1wbGUgaW5mb3JtYXRpb24sIGUuZy4sIHRvIHdoaWNoIGV4cGVyaW1lbnRhbCBncm91cCBzYW1wbGVzIGJlbG9uZykuICAKCmBgYHtyIHByaW50X21ldGFkYXRhX3VzZWR9CiMgQ29uZGl0aW9uYWxseSBzb3J0IGJ5IGRvc2UgaWYgdGhhdCBjb2x1bW4gbmFtZSBpcyBwcmVzZW50CmlmKGFueShncmVwbCh4PWNvbG5hbWVzKERFU2VxRGVzaWduKSwgcGF0dGVybj0iZG9zZSIsIGlnbm9yZS5jYXNlID0gVCkpKXsKICBkb3NlQ29sIDwtIGdyZXAoeD1jb2xuYW1lcyhERVNlcURlc2lnbiksIHBhdHRlcm49ImRvc2UiLCBpZ25vcmUuY2FzZSA9IFQpCiAga25pdHI6OmthYmxlKERFU2VxRGVzaWduICU+JSBncm91cF9ieSghIXN5bShwYXJhbXMkZGVzaWduKSkgJT4lIGFycmFuZ2UoZG9zZUNvbCwgLmJ5X2dyb3VwID0gVCksCiAgICAgICAgICAgICByb3cubmFtZXMgPSAgRiwKICAgICAgICAgICAgIGNhcHRpb249IlNhbXBsZXMgYW5kIGNvcnJlc3BvbmRpbmcgZXhwZXJpbWVudGFsIGNvbmRpdGlvbnMgdXNlZCBpbiB0aGlzIHJlcG9ydCIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIsICJyZXNwb25zaXZlIikpICU+JQogIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIsIGhlaWdodCA9ICI0ODBweCIpCn0gZWxzZSB7CiAga25pdHI6OmthYmxlKERFU2VxRGVzaWduICU+JSBhcnJhbmdlKCEhc3ltKHBhcmFtcyRkZXNpZ24pKSwKICAgICAgICAgICAgIHJvdy5uYW1lcyA9ICBGLAogICAgICAgICAgICAgY2FwdGlvbj0iU2FtcGxlcyBhbmQgY29ycmVzcG9uZGluZyBleHBlcmltZW50YWwgY29uZGl0aW9ucyB1c2VkIGluIHRoaXMgcmVwb3J0IikgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSkgJT4lCiAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIiwgaGVpZ2h0ID0gIjQ4MHB4IikKfQoKa25pdHI6OmthYmxlKERFU2VxRGVzaWduICU+JSBncm91cF9ieSghIXN5bShwYXJhbXMkZGVzaWduKSkgJT4lIHRhbGx5KCksCiAgICAgICAgICAgICByb3cubmFtZXMgPSBGLAogICAgICAgICAgICAgY29sLm5hbWVzID0gYygiRXhwZXJpbWVudGFsIGdyb3VwIiwgIk51bWJlciBvZiBzYW1wbGVzIGluIGdyb3VwIiksCiAgICAgICAgICAgICBjYXB0aW9uPSJOdW1iZXIgb2Ygc2FtcGxlcyBpbiBlYWNoIGV4cGVyaW1lbnRhbCBncm91cCB1c2VkIGluIHRoaXMgcmVwb3J0IikgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIiwgZnVsbF93aWR0aCA9IEYsIHBvc2l0aW9uID0gImxlZnQiKSAlPiUKICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiLCBoZWlnaHQgPSAiNDgwcHgiKQpgYGAKCiMjIyBTYW1wbGVzIHJlbW92ZWQgZnJvbSB0aGlzIHJlcG9ydAoKVGhpcyB0YWJsZSBzaG93cyB0aGUgc2FtcGxlcyB0aGF0IHdlcmUgcmVtb3ZlZCBmcm9tIHRoaXMgYW5hbHlzaXMuCgpgYGB7ciBwcmludF9tZXRhZGF0YV9yZW1vdmVkfQojIENvbmRpdGlvbmFsbHkgc29ydCBieSBkb3NlIGlmIHRoYXQgY29sdW1uIG5hbWUgaXMgcHJlc2VudD8Ka25pdHI6OmthYmxlKERFU2VxRGVzaWduQXNSZWFkICU+JSBhcnJhbmdlKCEhc3ltKHBhcmFtcyRkZXNpZ24pKSAlPiUgZHBseXI6OmZpbHRlcighb3JpZ2luYWxfbmFtZXMgJWluJSBERVNlcURlc2lnbiRvcmlnaW5hbF9uYW1lcyksCiAgICAgICAgICAgICByb3cubmFtZXMgPSAgRiwKICAgICAgICAgICAgIGNhcHRpb249IlNhbXBsZXMgcmVtb3ZlZCBmcm9tIGFuYWx5c2lzIikgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSkgJT4lCiAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIiwgaGVpZ2h0ID0gIjQ4MHB4IikKCmtuaXRyOjprYWJsZShERVNlcURlc2lnbkFzUmVhZCAlPiUKICAgICAgICAgICAgICAgZHBseXI6OmZpbHRlcihvcmlnaW5hbF9uYW1lcyAlaW4lIHJlbW92ZWQpICU+JQogICAgICAgICAgICAgICBncm91cF9ieSghIXN5bShwYXJhbXMkZGVzaWduKSkgJT4lIHRhbGx5KCksCiAgICAgICAgICAgICByb3cubmFtZXMgPSBGLAogICAgICAgICAgICAgY29sLm5hbWVzID0gYygiRXhwZXJpbWVudGFsIGdyb3VwIiwgIk51bWJlciBvZiBzYW1wbGVzIHJlbW92ZWQiKSwKICAgICAgICAgICAgIGNhcHRpb249Ik51bWJlciBvZiBzYW1wbGVzIHJlbW92ZWQgZnJvbSBlYWNoIGV4cGVyaW1lbnRhbCBncm91cCIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIsIGZ1bGxfd2lkdGggPSBGLCBwb3NpdGlvbiA9ICJsZWZ0IikgJT4lCiAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIiwgaGVpZ2h0ID0gIjQ4MHB4IikKYGBgCgojIyMgT3JpZ2luYWwgc2FtcGxlIG1ldGFkYXRhIGFzIHByb3ZpZGVkCgpUaGlzIHRhYmxlIHNob3dzIHRoZSBvcmlnaW5hbCBzYW1wbGUgbWV0YWRhdGEgaW5jbHVkaW5nIGFueSBzYW1wbGVzIHRoYXQgd2VyZSByZW1vdmVkIHdpdGhpbiB0aGlzIHJlcG9ydC4KCmBgYHtyIHByaW50X21ldGFkYXRhX3Byb3ZpZGVkfQojIENvbmRpdGlvbmFsbHkgc29ydCBieSBkb3NlIGlmIHRoYXQgY29sdW1uIG5hbWUgaXMgcHJlc2VudD8Ka25pdHI6OmthYmxlKERFU2VxRGVzaWduQXNSZWFkICU+JSBhcnJhbmdlKCEhc3ltKHBhcmFtcyRkZXNpZ24pKSwKICAgICAgICAgICAgIHJvdy5uYW1lcyA9ICBGLAogICAgICAgICAgICAgY2FwdGlvbj0iQ29tcGxldGUgc2FtcGxlIG1ldGFkYXRhIGFzIHByb3ZpZGVkIHRvIHRoZSBHZW5vbWljcyBMYWJvcmF0b3J5LiIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIsICJyZXNwb25zaXZlIikpICU+JQogIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIsIGhlaWdodCA9ICI0ODBweCIpCgprbml0cjo6a2FibGUoREVTZXFEZXNpZ25Bc1JlYWQgJT4lIGdyb3VwX2J5KCEhc3ltKHBhcmFtcyRkZXNpZ24pKSAlPiUgdGFsbHkoKSwKICAgICAgICAgICAgIHJvdy5uYW1lcyA9IEYsCiAgICAgICAgICAgICBjb2wubmFtZXMgPSBjKCJFeHBlcmltZW50YWwgZ3JvdXAiLCAiTnVtYmVyIG9mIHNhbXBsZXMgYXMgcHJvdmlkZWQiKSwKICAgICAgICAgICAgIGNhcHRpb249Ik51bWJlciBvZiBzYW1wbGVzIGFuZCBleHBlcmltZW50YWwgZ3JvdXBzIGFzIHByb3ZpZGVkIikgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIiwgZnVsbF93aWR0aCA9IEYsIHBvc2l0aW9uID0gImxlZnQiKSAlPiUKICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiLCBoZWlnaHQgPSAiNDgwcHgiKQpgYGAKCiMjIExvYWQgUi1PREFGIGZ1bmN0aW9ucwoKYGBge3IgJ2RlZmluZS1PREFGLWZ1bmN0aW9ucycsIGNvbGxhcHNlPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiNERUZJTkUgRlVOQ1RJT05TCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnBsb3QuYmFycGxvdHM8LWZ1bmN0aW9uKHNhbXBsZXMsYikgewogIGNvbG9yIDwtIE5VTEwKICBmb3IgKGggaW4gMTpuY29sKG5vcm1fZGF0YSkpewogICAgaWYgKHN1YnN0cmluZyhjb2xuYW1lcyhub3JtX2RhdGEpW2hdLCAxLCAzKSA9PSBzdWJzdHJpbmcoY29uZGl0aW9uMiwgMSwgMykpIHsgY29sb3IgPC0gYyhjb2xvciwgInJlZDMiKSB9IGVsc2UgeyBjb2xvciA8LSBjKGNvbG9yLCAiZGFya2dyZXkiKX0KICB9CiAgZmlsZU5hbWVQbG90IDwtIHBhc3RlMChiLCByb3cubmFtZXMoc2FtcGxlcylbYl0sICIucG5nIikKICBwc2V1ZG9UaXRsZSA8LSBwYXN0ZTAocm93Lm5hbWVzKHNhbXBsZXMpW2JdLCAiX3BBZGo6Iiwgc2FtcGxlc1tiLCJwYWRqIl0pCiAgCiAgcG5nKGZpbGU9cGFzdGUoZmlsZU5hbWVQbG90LCBzZXA9Ii8iKSwgd2lkdGg9MTIwMCwgaGVpZ2h0PTcwMCwgcG9pbnRzaXplPTIwKQogIHBhcihtYXI9Yyg4LDQsMywxKSkKICBiYXJwbG90KGFzLm51bWVyaWMobm9ybV9kYXRhW3Jvdy5uYW1lcyhzYW1wbGVzKVtiXSxdKSwgbGFzPTIsIGNvbD1jb2xvciwgbWFpbj1wc2V1ZG9UaXRsZSwgY2V4Lm5hbWVzPTAuNSwgIGNleC5heGlzPTAuOCwgbmFtZXMuYXJnPWNvbG5hbWVzKG5vcm1fZGF0YSkpIAogIGRldi5vZmYoKQp9ICNwbG90LmJhcnBsb3RzIGZ1bmN0aW9uIGRvbmUKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCmRyYXcuYmFycGxvdHM8LWZ1bmN0aW9uKHNhbXBsZXMsIHRvcF9ib3R0b20sIE5VTSl7CiAgaWYgKG5yb3coc2FtcGxlcykgPT0gMCkgewogICAgI3ByaW50KCJubyBnZW5lcyB0byBwbG90IikgCiAgfSBlbHNlIHsgCiAgICBpZiAodG9wX2JvdHRvbSA9PSAidG9wIikgewogICAgICAjcHJpbnQocGFzdGUwKCJkcmF3aW5nIFRvcCAiLCBOVU0sICIgcGxvdHMiKSkKICAgICAgaWYgKG5yb3coc2FtcGxlcykgPD0gTlVNKSB7IAogICAgICAgIGZvciAoYiBpbiAxOm5yb3coc2FtcGxlcykpIHtwbG90LmJhcnBsb3RzKHNhbXBsZXMsYil9CiAgICAgIH0KICAgICAgCiAgICAgIGlmIChucm93KHNhbXBsZXMpID4gTlVNKSB7IAogICAgICAgIGZvciAoYiBpbiAxOk5VTSkge3Bsb3QuYmFycGxvdHMoc2FtcGxlcyxiKX0JCiAgICAgIH0JCiAgICB9CiAgICAKICAgIGlmICh0b3BfYm90dG9tID09ICJib3R0b20iKSB7CiAgICAgICNwcmludChwYXN0ZTAoImRyYXdpbmcgQm90dG9tIiwgTlVNLCAiIHBsb3RzIikpCiAgICAgIGlmIChucm93KHNhbXBsZXMpIDw9IE5VTSkgeyAKICAgICAgICBmb3IgKGIgaW4gMTpucm93KHNhbXBsZXMpKSB7cGxvdC5iYXJwbG90cyhzYW1wbGVzLGIpfQogICAgICB9CiAgICAgIGlmIChucm93KHNhbXBsZXMpID4gTlVNKSB7IAogICAgICAgIGZvciAoYiBpbiAoKG5yb3coc2FtcGxlcyktTlVNKzEpOm5yb3coc2FtcGxlcykpKSB7cGxvdC5iYXJwbG90cyhERXNhbXBsZXMsYil9CiAgICAgIH0KICAgIH19Cn0gI2RyYXcuYmFycGxvdHMgZnVuY3Rpb24gZG9uZQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKYGBgCgpUaGUgY29kZSBhYm92ZSAobm90IHNob3duIGJ5IGRlZmF1bHQpIGxvYWRzIGluIHBsb3R0aW5nIGZ1bmN0aW9ucyBzcGVjaWZpYyB0byB0aGUgT21pY3MgRGF0YSBBbmFseXNpcyBGcmFtZXdvcmtzIGZvciBSZWd1bGF0b3J5IGFwcGxpY2F0aW9uIChSLU9EQUYpIHRlbXBsYXRlLiBNb3JlIGluZm9ybWF0aW9uIG9uIHRoZSBSLU9EQUYgZnJhbWV3b3JrIGlzIGF2YWlsYWJsZSBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL01DVHZlcmhlaWplbi9SLU9EQUYpLgoKIyMgUnVuIHBhaXJ3aXNlIGNvbXBhcmlzb25zCgpgYGB7ciAncnVuX09EQUZfY29kZScsIGNvbGxhcHNlPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGV2YWw9ZmxhZ30KCiMgSW5pdGlhbCBzZXR1cCBmb3IgREVTZXEyIGNvbnRyYXN0cwpjb29rcyAgIDwtIEZBTFNFICMgTm9ybWFsbHkgc2V0IENvb2sncyBjdXRvZmYgdG8gZmFsc2UKcmVzTGlzdCA8LSBsaXN0KCkKQ291bnRzICA8LSBjb3VudHMoZGRzLCBub3JtYWxpemVkPVRSVUUpCkNQTWRkcyAgPC0gY3BtKGNvdW50cyhkZHMsIG5vcm1hbGl6ZWQ9VFJVRSkpCgpmb3IgKHggaW4gMTpucm93KGNvbnRyYXN0cykpIHsJIyMgZm9yIGFsbCBjb21wYXJpc29ucyB0byBiZSBkb25lCQoJCiAgY29uZGl0aW9uMSA8LSBjb250cmFzdHNbeCwyXQoJY29uZGl0aW9uMiA8LSBjb250cmFzdHNbeCwxXQoJcHJpbnQocGFzdGUoY29uZGl0aW9uMiwgIiB2cyAiLCBjb25kaXRpb24xLCAiOiIsIE5PUk1fVFlQRSkpCgkKCURFX0Rlc2lnbiA8LSBtYXRyaXgoZGF0YT1OQSwgbmNvbD0yKQoJREVfRGVzaWduIDwtIGFzLm1hdHJpeChERVNlcURlc2lnbltjKGdyZXAoY29uZGl0aW9uMSxERVNlcURlc2lnblssREVTSUdOXSksIGdyZXAoY29uZGl0aW9uMixERVNlcURlc2lnblssREVTSUdOXSkpLF0pCglzYW1wbGVzICAgPC0gc2FtcGxlRGF0YVssIHJvd25hbWVzKERFX0Rlc2lnbildCgljb2xuYW1lcyhzYW1wbGVzKSA8LSBOVUxMCgkKCSMjIyMjIyMjIyMjCgoJcHJpbnQocGFzdGUwKCJGaWx0ZXJpbmcgZ2VuZXM6IDc1JSBvZiBhdCBsZWFzdCAxIGdyb3VwIG5lZWQgdG8gYmUgYWJvdmUgIiwgTWluQ291bnQsICIgQ1BNIikpCglwcmludCgiQU5EIikKCXByaW50KCJEZXRlY3Rpbmcgc3B1cmlvdXMgc3Bpa2VzOiBNYXgtTWVkaWFuID4gU3VtLyhSZXArMSkiICkKCQlTYW1wUGVyR3JvdXAgPC0gdGFibGUoREVfRGVzaWduWyxERVNJR05dKQoJCWlkeCAgICAgICAgICA8LSBGbGFnU3Bpa2UgPC0gTmFtZVJvd3MgPC0gTlVMTAoJCWZvciAoZ2VuZSBpbiAxOm5yb3coZGRzKSkgeyAjIExvb3Agb3ZlciBlYWNoIGdlbmUKCQkJR3JvdXBzUGFzcyA8LSBjaGVja1NwaWtlIDwtIE5VTEwKCQkJZm9yIChncm91cCBpbiAxOmxlbmd0aChTYW1wUGVyR3JvdXApKSB7ICMgVGVzdCBpZiBncm91cCBwYXNzZXMKCQkJCXNhbXBsZUNvbHMgPC0gZ3JlcChkaW1uYW1lcyhTYW1wUGVyR3JvdXApW1sxXV1bZ3JvdXBdLCBERV9EZXNpZ25bLERFU0lHTl0pCgkJCQlDaGVjayAgICAgIDwtIHN1bShDUE1kZHNbZ2VuZSwgc2FtcGxlQ29sc10gPj0gTWluQ291bnQpID49IDAuNzUqU2FtcFBlckdyb3VwW2dyb3VwXQoJCQkJR3JvdXBzUGFzcyA8LSBjKEdyb3Vwc1Bhc3MsIENoZWNrKQoJCQkJaWYgKENoZWNrID09IEZBTFNFKSB7Y2hlY2tTcGlrZSA8LSBjKGNoZWNrU3Bpa2UsIENoZWNrKX0gZWxzZSB7CgkJCQkJY2hlY2tTcGlrZSA8LSBjKGNoZWNrU3Bpa2UsICgobWF4KENvdW50c1tnZW5lLCBzYW1wbGVDb2xzXSkgLSBtZWRpYW4oQ291bnRzW2dlbmUsc2FtcGxlQ29sc10pKSA+PQoJCQkJCSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKHN1bShDb3VudHNbZ2VuZSwgc2FtcGxlQ29sc10pIC8gKFNhbXBQZXJHcm91cFtncm91cF0rMSkpKSkKCQkJCX0KCQkJfQoJCQlpZHggPC0gYyhpZHgsIGFzLmxvZ2ljYWwoc3VtKEdyb3Vwc1Bhc3MpKSkKCQkJaWYgKHN1bShjaGVja1NwaWtlKSA+PTEpIHsKCQkJCUZsYWdTcGlrZSA8LSByYmluZChGbGFnU3Bpa2UsIENvdW50c1tnZW5lLF0pCgkJCQlOYW1lUm93cyAgPDwtIGMoTmFtZVJvd3MsIHJvdy5uYW1lcyhDb3VudHMpW2dlbmVdKQoJCQkJcm93Lm5hbWVzKEZsYWdTcGlrZSkgPC0gTmFtZVJvd3MgCgkJCX0KCQl9CgoJcHJpbnQoIk9idGFpbmluZyB0aGUgREVTZXEyIHJlc3VsdHMiKQoJY3VycmVudENvbnRyYXN0IDwtIGMoREVTSUdOLCBjb25kaXRpb24yLCBjb25kaXRpb24xKQoJcmVzIDwtIHJlc3VsdHMoZGRzW2lkeF0sCgkgICAgICAgICAgICAgICBwYXJhbGxlbCA9IFRSVUUsIEJQUEFSQU09TXVsdGljb3JlUGFyYW0oMzkpLAoJICAgICAgICAgICAgICAgY29udHJhc3Q9Y3VycmVudENvbnRyYXN0LAoJICAgICAgICAgICAgICAgcEFkanVzdE1ldGhvZD0gJ2ZkcicsCgkgICAgICAgICAgICAgICBjb29rc0N1dG9mZj1jb29rcykgIyBDb29rcyBjdXRvZmYgZGlzYWJsZWQgLSBtYW51YWxseSBpbnNwZWN0LgoJcmVzIDwtIGxmY1NocmluayhkZHM9ZGRzW2lkeF0sCgkgICAgICAgICAgICAgICAgIGNvbnRyYXN0PWN1cnJlbnRDb250cmFzdCwKCSAgICAgICAgICAgICAgICAgcmVzPXJlcywKCSAgICAgICAgICAgICAgICAgdHlwZT0iYXNociIpCgkjTWFrZSBuZXcgZGlyZWN0b3J5IGZvciB0aGUgT0RBRi1zcGVjaWZpYyBmaWxlcwoJT0RBRmRpciA8LSBmaWxlLnBhdGgocGF0aHMkREVHX291dHB1dCwgIlItT0RBRiIpCglpZighZGlyLmV4aXN0cyhPREFGZGlyKSkge2Rpci5jcmVhdGUoT0RBRmRpciwgcmVjdXJzaXZlID0gVFJVRSl9CglzZXR3ZChPREFGZGlyKQoJRmlsZU5hbWUgPC0gcGFzdGUoTk9STV9UWVBFLCBjb25kaXRpb24yLCJ2cyIsY29uZGl0aW9uMSwgIkZEUiIsIHBBZGpWYWx1ZSwgc2VwPSJfIikJCQoJI1NhdmUgb3V0cHV0IHRhYmxlcwkJCglub3JtX2RhdGEgPDwtIGNvdW50cyhkZHNbaWR4XSxub3JtYWxpemVkPVRSVUUpCgljb2xuYW1lcyhub3JtX2RhdGEpIDwtIGNvbERhdGEoZGRzKVssREVTSUdOXQoJd3JpdGUudGFibGUobm9ybV9kYXRhLGZpbGU9cGFzdGUwKEZpbGVOYW1lLCAiX05vcm1fRGF0YS50eHQiKSwgc2VwPSJcdCIsIHF1b3RlPUZBTFNFKQoJd3JpdGUudGFibGUoRmxhZ1NwaWtlLGZpbGU9cGFzdGUwKEZpbGVOYW1lLCAiX0ZsYWdnZWRTcGlrZXMudHh0IiksIHNlcD0iXHQiLCBxdW90ZT1GQUxTRSkKCURFc2FtcGxlcyA8PC0gc3Vic2V0KHJlcyxyZXMkcGFkaiA8IHBBZGpWYWx1ZSkJCgl3cml0ZS50YWJsZShERXNhbXBsZXMsZmlsZT1wYXN0ZTAoRmlsZU5hbWUsIl9ERUdfdGFibGUudHh0IiksIHNlcD0iXHQiLCBxdW90ZT1GQUxTRSkKCURFc3Bpa2VzIDw8LSBERXNhbXBsZXNbcm93bmFtZXMoREVzYW1wbGVzKSVpbiVOYW1lUm93cyxdCQoJd3JpdGUudGFibGUoREVzcGlrZXMsZmlsZT1wYXN0ZTAoRmlsZU5hbWUsIl9ERXNwaWtlc190YWJsZS50eHQiKSwgc2VwPSJcdCIscXVvdGU9RkFMU0UpCgoJcmVzTGlzdFtbeF1dIDwtIHJlcwoJCglpZiAoUl9PREFGX3Bsb3RzPT1UUlVFKSB7CiAgCXByaW50KCJjcmVhdGluZyBSZWFkIGNvdW50IFBsb3RzIikKICAJIyB0b3AgREVHcwogIAlwbG90ZGlyIDwtIGZpbGUucGF0aChwYXRocyRERUdfb3V0cHV0LCAicGxvdHMiKQogIAlpZighZGlyLmV4aXN0cyhwbG90ZGlyKSkge2Rpci5jcmVhdGUocGxvdGRpciwgcmVjdXJzaXZlID0gVFJVRSl9CiAgCWJhcnBsb3QuZGlyIDwtIGZpbGUucGF0aChwYXRocyRERUdfb3V0cHV0LCAicGxvdHMiLCAiL2JhcnBsb3RfZ2VuZXMvIikKICAJaWYoIWRpci5leGlzdHMoYmFycGxvdC5kaXIpKSB7ZGlyLmNyZWF0ZShiYXJwbG90LmRpciwgcmVjdXJzaXZlID0gVFJVRSl9CiAgCiAgCVRPUGJhcnBsb3QuZGlyIDwtIGZpbGUucGF0aChiYXJwbG90LmRpciwgIlRvcF9ERUdzIikKICAJaWYoIWRpci5leGlzdHMoVE9QYmFycGxvdC5kaXIpKSB7ZGlyLmNyZWF0ZShUT1BiYXJwbG90LmRpciwgcmVjdXJzaXZlID0gVFJVRSl9CiAgCXNldHdkKFRPUGJhcnBsb3QuZGlyKQogIAlkcmF3LmJhcnBsb3RzKERFc2FtcGxlcywgInRvcCIsIDIwKSAjIChERXNhbXBsZXMsIHRvcF9ib3R0b20sIE5VTSkKICAJcHJpbnQoIlRvcCAyMCBERUcgcGxvdHMgZG9uZSIpCiAgCiAgCSMgU3B1cmlvdXMgc3Bpa2VzCiAgCVNQSUtFYmFycGxvdC5kaXIgPC0gZmlsZS5wYXRoKGJhcnBsb3QuZGlyLCAiREVfU3B1cmlvdXNfc3Bpa2VzIikKICAJaWYoIWRpci5leGlzdHMoU1BJS0ViYXJwbG90LmRpcikpIHtkaXIuY3JlYXRlKFNQSUtFYmFycGxvdC5kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpfQogIAlzZXR3ZChTUElLRWJhcnBsb3QuZGlyKQogIAlkcmF3LmJhcnBsb3RzKERFc3Bpa2VzLCAidG9wIiwgbnJvdyhERXNwaWtlcykpICMgKERFc2FtcGxlcywgdG9wX2JvdHRvbSwgTlVNKQogIAlwcmludCgiQWxsIERFX1NwdXJpb3VzX3NwaWtlIHBsb3RzIGRvbmUiKQoJfQp9CmBgYAoKVGhlIGNvZGUgYWJvdmUgKG5vdCBzaG93biBieSBkZWZhdWx0KSBydW5zIHRoZSBSLU9EQUYgc3B1cmlvdXMgc3Bpa2UgZGV0ZWN0aW9uIGFuZCBvdXRwdXRzIHRoZSBERVNlcSBSZXN1bHRzIG9iamVjdHMgYXMgYSBsaXN0IGZvciBlYWNoIGNvbnRyYXN0LiBBcyBzcGVjaWZpZWQgYnkgdGhlIFItT0RBRiBndWlkZWxpbmVzLCA3NSUgb2YgYXQgbGVhc3QgMSBncm91cCBuZWVkIHRvIGJlIGFib3ZlIGByIE1pbkNvdW50YCBDUE0gYW5kIHNwdXJpb3VzIHNwaWtlcyB3ZXJlIHJlbW92ZWQgaW4gd2hpY2ggTWF4LU1lZGlhbiA+IFN1bS8oUmVwKzEpLiAgCgpUaGUgbG9nMkZvbGRDaGFuZ2Ugc2hyaW5rYWdlIHByb2NlZHVyZSB1c2VkIHdhcyBgciByZXNMaXN0W1sxXV1AcHJpb3JJbmZvJHR5cGVgLiBBbiBhbHBoYSBvZiBgciByZXNMaXN0W1sxXV1AbWV0YWRhdGEkYWxwaGFgIHdhcyB1c2VkIHRvIGV4dHJhY3QgcmF3IHJlc3VsdHMsIHdoaWNoIGFyZSByZXBvcnRlZCBhcyB0aGUgYHIgZ3N1YihtY29scyh4PXJlc0xpc3RbWzFdXSkkZGVzY3JpcHRpb25bWzRdXSxwYXR0ZXJuPSI6LioiLHJlcGxhY2VtZW50PSIiKWAuIFRvIGFjY291bnQgZm9yIG11bHRpcGxlIHRlc3RpbmcsIGByIG1jb2xzKHJlc0xpc3RbWzFdXSkkZGVzY3JpcHRpb25bNV1gIGFyZSByZXBvcnRlZC4gQ29vaydzIGN1dG9mZiB3YXMgc2V0IHRvIGByIGNvb2tzYCBpbiB0aGlzIGFuYWx5c2lzLiAgCgoKIyMgQ3JlYXRlIHN1bW1hcnkgdGFibGVzCgpgYGB7ciBjcmVhdGVfdGFibGVzLCBjb2xsYXBzZT1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBldmFsPWZsYWd9CiNsaXN0RW5zZW1ibCgpCiNsaXN0TWFydHMoKQojbGlzdERhdGFzZXRzKHVzZU1hcnQoJ2Vuc2VtYmwnKSkKZW5zZW1ibCA8LSB1c2VNYXJ0KCJlbnNlbWJsIiwgZGF0YXNldCA9IGVuc2VtYmxfc3BlY2llcywgIGhvc3QgPSAidXNlYXN0LmVuc2VtYmwub3JnIikKI2xpc3RBdHRyaWJ1dGVzKGVuc2VtYmwpCiNsaXN0RmlsdGVycyhlbnNlbWJsKQoKZ2VuZXMgPC0gcm93Lm5hbWVzKHJlc0xpc3RbWzFdXSkKI2dlbmVzX2FsbCA8LSB1bmlxdWUocm93Lm5hbWVzKGFzc2F5KGRkcykpKQoKaWYgKGZpbGUuZXhpc3RzKGZpbGUucGF0aChwYXRocyRSRGF0YSwiaWRfdGFibGUuUmRhdGEiKSkpIHsKICBwcmludChwYXN0ZSgiQWxyZWFkeSBmb3VuZCBJRCB0YWJsZTsgc2tpcHBpbmcgYmlvbWFSdCBxdWVyeSBhbmQgbG9hZGluZyBmcm9tIGRpc2suIikpCiAgbG9hZChmaWxlLnBhdGgocGF0aHMkUkRhdGEsImlkX3RhYmxlLlJkYXRhIikpCiAgaWRfdGFibGUgPC0gaWRfdGFibGVfZW50cmV6WywxOjNdCiAgfSBlbHNlIHsKICBpZF90YWJsZV9lbnRyZXogPC0gZ2V0Qk0oZmlsdGVycz1iaW9tYXJ0X2ZpbHRlciwKICAgICAgICAgICAgICAgICAgICAgICAgYXR0cmlidXRlcz0gYygiZW5zZW1ibF9nZW5lX2lkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZXh0ZXJuYWxfZ2VuZV9uYW1lIiwgIyBtZ2lfc3ltYm9sIGZvciBNb3VzZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJkZXNjcmlwdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImVudHJlemdlbmVfaWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJlbnRyZXpnZW5lX2FjY2Vzc2lvbiIpLAogICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXM9Z2VuZXMsCiAgICAgICAgICAgICAgICAgICAgICAgIG1hcnQ9ZW5zZW1ibCkKICBzYXZlKGlkX3RhYmxlX2VudHJleiwgZmlsZT1maWxlLnBhdGgocGF0aHMkUkRhdGEsImlkX3RhYmxlLlJkYXRhIikpCiAgaWRfdGFibGUgPC0gaWRfdGFibGVfZW50cmV6WywxOjNdCn0KCiMgZ2VuZXNfZW50cmV6aWQgPC0gZHBseXI6OmxlZnRfam9pbihkYXRhLmZyYW1lKGdlbmVzKSwgaWRfdGFibGVfZW50cmV6LCBieT1jKCJnZW5lcyIgPSBiaW9tYXJ0X2ZpbHRlcikpICMgQ2FuIEkgcmVtb3ZlIHRoaXM/IEkgdGhpbmsgc28KCnNpZ3RhYkxpc3QgPC0gbGlzdCgpCmFsbHRhYmxMaXN0IDwtIGxpc3QoKQpmb3IgKGkgaW4gMTpsZW5ndGgocmVzTGlzdCkpIHsKICBwcmludChpKQogIHNpZ1RhYiA8LSByZXNMaXN0W1tpXV0KICAjIEFkZCB0YXhvbm9teQogIGlmIChucm93KHNpZ1RhYikgPT0gMCkgewogICAgbmV4dAogIH0gZWxzZSB7CiAgICBzaWdUYWIgPC0gY2JpbmQoZ2VuZXM9cm93Lm5hbWVzKHJlc0xpc3RbW2ldXSksCiAgICAgICAgICAgICAgICAgICAgYXMoc2lnVGFiLCAiZGF0YS5mcmFtZSIpLAogICAgICAgICAgICAgICAgICAgIGNvbnRyYXN0ID0gZ3N1YihwYXR0ZXJuID0gcGFzdGUwKCJsb2cyLioiLCBERVNJR04sICJcICIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlbWVudCA9ICAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IHJlc0xpc3RbW2ldXUBlbGVtZW50TWV0YWRhdGFbWzJdXVsyXSkpCiAgICBuYW1lcyhzaWdUYWIpW25hbWVzKHNpZ1RhYikgPT0gImdlbmVzIl0gPC0gYmlvbWFydF9maWx0ZXIKICAgIHNpZ1RhYiA8LSBkcGx5cjo6bGVmdF9qb2luKHNpZ1RhYiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkX3RhYmxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnk9YmlvbWFydF9maWx0ZXIpCiAgICBzaWdUYWIgPC0gZHBseXI6Om11dGF0ZShzaWdUYWIsIGxpbmVhckZvbGRDaGFuZ2U9aWZlbHNlKGxvZzJGb2xkQ2hhbmdlID4gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMiBeIGxvZzJGb2xkQ2hhbmdlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtMSAvICgyIF4gbG9nMkZvbGRDaGFuZ2UpKSkKICAgICNzaWdUYWIgPC0gc2lnVGFiWyxjKDEsOSwxMCwyLDMsMTEsNTo4KV0gIyBSZW9yZGVyIGNvbHVtbnMKICAgIHNpZ1RhYiA8LSBzaWdUYWJbLGMoMSw4LDksMiwzLDEwLDQ6NyldCiAgICBhbGx0YWJsTGlzdFtbaV1dIDwtIHNpZ1RhYgogICAgc2lnVGFiIDwtIHNpZ1RhYlshaXMubmEoc2lnVGFiJHBhZGopICYgc2lnVGFiJHBhZGogPCBhbHBoYSAmIGFicyhzaWdUYWIkbGluZWFyRm9sZENoYW5nZSkgPiBsaW5lYXJfZmNfZmlsdGVyLCBdICMjIEZJTFRFUlMhCiAgICBzaWd0YWJMaXN0W1tpXV0gPC0gc2lnVGFiICU+JSBkcGx5cjo6ZGlzdGluY3QoKQogIH0KfQojIFdyaXRlIGRhdGFmcmFtZSBvZiBhbGwgcmVzdWx0cwojIFNpZ25pZmljYW50IG9ubHkKc2lndGFiTGlzdCA8LSBzaWd0YWJMaXN0WyFzYXBwbHkoc2lndGFiTGlzdCwgaXMubnVsbCldCnNpZ25pZmljYW50UmVzdWx0cyA8LSByYmluZGxpc3Qoc2lndGFiTGlzdCkKc2lnbmlmaWNhbnRSZXN1bHRzIDwtIGRwbHlyOjpkaXN0aW5jdChzaWduaWZpY2FudFJlc3VsdHMpCnNpZ25pZmljYW50UmVzdWx0cyRjb250cmFzdCA8LSBmYWN0b3Ioc2lnbmlmaWNhbnRSZXN1bHRzJGNvbnRyYXN0KQojIEFsbCByZXN1bHRzIGluY2x1ZGluZyBub24tc2lnbmlmaWNhbnQKYWxsUmVzdWx0cyA8LSByYmluZGxpc3QoYWxsdGFibExpc3QpCgojIEFsbCBSZXN1bHRzIGZvciBQbG90dGluZwojIFJlcGxhY2UgTkEgZ2VuZSBzeW1ib2xzIHdpdGggZW5zZW1ibCBJRAphbGxSZXN1bHRzJGV4dGVybmFsX2dlbmVfbmFtZVtpcy5uYShhbGxSZXN1bHRzJGV4dGVybmFsX2dlbmVfbmFtZSldIDwtIGFsbFJlc3VsdHMkZW5zZW1ibF9nZW5lX2lkW2lzLm5hKGFsbFJlc3VsdHMkZXh0ZXJuYWxfZ2VuZV9uYW1lKV0gCiMgUmVwbGFjZSBibGFuayBnZW5lIHN5bWJvbHMgd2l0aCBlbnNlbWJsIElECmFsbFJlc3VsdHMkZXh0ZXJuYWxfZ2VuZV9uYW1lW2FsbFJlc3VsdHMkZXh0ZXJuYWxfZ2VuZV9uYW1lID09ICIiXSAgPC0gYWxsUmVzdWx0cyRlbnNlbWJsX2dlbmVfaWRbYWxsUmVzdWx0cyRleHRlcm5hbF9nZW5lX25hbWUgPT0gIiJdCgpJUEEgPC0gYWxsUmVzdWx0cyAlPiUgCiAgZGlzdGluY3QoKSAlPiUgCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGNvbnRyYXN0LCB2YWx1ZXNfZnJvbSA9IGMobG9nMkZvbGRDaGFuZ2UsbGluZWFyRm9sZENoYW5nZSxsZmNTRSxwdmFsdWUscGFkaikpCmFsbFJlc3VsdHMkcGFkalthbGxSZXN1bHRzJHBhZGogPT0gMF0gPC0gMTBeLTEwMCAjIEZvciBwbG90dGluZyBwdXJwb3NlcyEKYWxsUmVzdWx0c09yZGVyZWRfbG9nRkNfZmlsdGVyIDwtIGRwbHlyOjpmaWx0ZXIoYWxsUmVzdWx0cywgYWJzKGxpbmVhckZvbGRDaGFuZ2UpID4gMS41KSAlPiUKICBhcnJhbmdlKC1hYnMobGluZWFyRm9sZENoYW5nZSkpCmFsbFJlc3VsdHNPcmRlcmVkX2xvZ0ZDX2ZpbHRlciA8LSBkcGx5cjo6ZmlsdGVyKGFsbFJlc3VsdHNPcmRlcmVkX2xvZ0ZDX2ZpbHRlciwgcGFkaiA8IGFscGhhKQpyZXMuZGYgPC0gYWxsUmVzdWx0c09yZGVyZWRfbG9nRkNfZmlsdGVyCgpkZWdUYWJsZSA8LSBzaWduaWZpY2FudFJlc3VsdHMgJT4lIAogIGRwbHlyOjpncm91cF9ieShjb250cmFzdCkgJT4lCiAgZHBseXI6OmNvdW50KCkKCmxlbmd0aHMgPC0gbGFwcGx5KHJlc0xpc3QsIG5yb3cpCmxvbmdlc3QgPC0gd2hpY2gubWF4KGxlbmd0aHMpCnN1bW1hcnlUYWJsZSA8LSBkYXRhLmZyYW1lKCBnZW5lcz1yb3cubmFtZXMocmVzTGlzdFtbbG9uZ2VzdF1dKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJhc2VNZWFuPXJlc0xpc3RbW2xvbmdlc3RdXSRiYXNlTWVhbiApCm5hbWVzKHN1bW1hcnlUYWJsZSlbbmFtZXMoc3VtbWFyeVRhYmxlKSA9PSAiZ2VuZXMiXSA8LSBiaW9tYXJ0X2ZpbHRlcgpjb250cmFzdHNJblN1bW1hcnkgPC0gdmVjdG9yKCkKZm9yIChpIGluIDE6bGVuZ3RoKHJlc0xpc3QpKSB7CiAgcHJpbnQoaSkKICBuIDwtIHJlc0xpc3RbW2ldXUBlbGVtZW50TWV0YWRhdGFbWzJdXVsyXQogIG4gPC0gZ3N1YihwYXR0ZXJuID0gcGFzdGUwKCJsb2cyXCBmb2xkXCBjaGFuZ2VcIFxcKE1NU0VcXCk6XCAiLERFU0lHTiksICMgTWlnaHQgbmVlZCB0byBiZSBNTEUgZm9yIHNvbWUgdGVzdHMKICAgICAgICAgICAgcmVwbGFjZW1lbnQgPSAgcGFzdGUwKCJsb2cyIEZvbGQgQ2hhbmdlIiksCiAgICAgICAgICAgIHggPSByZXNMaXN0W1tpXV1AZWxlbWVudE1ldGFkYXRhW1syXV1bMl0pCiAgcCA8LSBnc3ViKHBhdHRlcm4gPSBwYXN0ZTAoImxvZzJcIGZvbGRcIGNoYW5nZVwgXFwoTU1TRVxcKTpcICIsREVTSUdOKSwKICAgICAgICAgICByZXBsYWNlbWVudCA9ICByZXNMaXN0W1tpXV1AZWxlbWVudE1ldGFkYXRhW1syXV1bNl0sICMgTm90IHVzZWQ/CiAgICAgICAgICAgeCA9IHJlc0xpc3RbW2ldXUBlbGVtZW50TWV0YWRhdGFbWzJdXVsyXSkKICBxIDwtIGdzdWIocGF0dGVybiA9IHBhc3RlMCgibG9nMlwgZm9sZFwgY2hhbmdlXCBcXChNTVNFXFwpOlwgIixERVNJR04sIlwgIiksCiAgICAgICAgICAgIHJlcGxhY2VtZW50ID0gICIiLAogICAgICAgICAgICB4ID0gcmVzTGlzdFtbaV1dQGVsZW1lbnRNZXRhZGF0YVtbMl1dWzJdKQogIG1lc3NhZ2UobikKICBtZXNzYWdlKHApCiAgbWVzc2FnZShxKQogIHRvSm9pbiA8LSBhcy5kYXRhLmZyYW1lKHJlc0xpc3RbW2ldXSkKICBzZXREVCh0b0pvaW4sIGtlZXAucm93bmFtZXMgPSBUKVtdCiAgc2V0bmFtZXModG9Kb2luLCAxLCBiaW9tYXJ0X2ZpbHRlcikKICB0b0pvaW4gPC0gbXV0YXRlKHRvSm9pbiwgbGluZWFyRm9sZENoYW5nZT1pZmVsc2UobG9nMkZvbGRDaGFuZ2UgPiAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAyIF4gbG9nMkZvbGRDaGFuZ2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC0xIC8gKDIgXiBsb2cyRm9sZENoYW5nZSkpKQogIHRvSm9pbiA8LSB0b0pvaW5bLGMoMTozLDcsNDo2KV0KICBzdW1tYXJ5VGFibGUgPC0gZHBseXI6OmxlZnRfam9pbihzdW1tYXJ5VGFibGUsIGRwbHlyOjpzZWxlY3QodG9Kb2luLCAhYyhiYXNlTWVhbixwdmFsdWUsbGZjU0UpKSwgYnk9YmlvbWFydF9maWx0ZXIpCgogIG5hbWVzKHN1bW1hcnlUYWJsZSlbW25jb2woc3VtbWFyeVRhYmxlKS0yXV0gPC0gcGFzdGUwKCJsb2cyRm9sZENoYW5nZV8iLGkpCiAgbmFtZXMoc3VtbWFyeVRhYmxlKVtbbmNvbChzdW1tYXJ5VGFibGUpLTFdXSA8LSBwYXN0ZTAoImxpbmVhckZvbGRDaGFuZ2VfIixpKQogIG5hbWVzKHN1bW1hcnlUYWJsZSlbW25jb2woc3VtbWFyeVRhYmxlKV1dIDwtIHBhc3RlMCgiRkRSXyIsaSkKICBjb250cmFzdHNJblN1bW1hcnlbaV0gPC0gcQogIHByaW50KHN1bW1hcnkocmVzTGlzdFtbaV1dLCBwQWRqVmFsdWUpKQp9CgptYXhGQ3MgPC0gYWxsUmVzdWx0cyAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoISFzeW0oYmlvbWFydF9maWx0ZXIpKSAlPiUKICBkcGx5cjo6ZmlsdGVyKGFicyhsaW5lYXJGb2xkQ2hhbmdlKSA9PSBtYXgoYWJzKGxpbmVhckZvbGRDaGFuZ2UpKSkgJT4lCiAgZHBseXI6OnVuZ3JvdXAoKSAlPiUKICBkcGx5cjo6c2VsZWN0KCEhc3ltKGJpb21hcnRfZmlsdGVyKSwgbGluZWFyRm9sZENoYW5nZSkKCm1pblB2YWxzIDwtIGFsbFJlc3VsdHMgJT4lCiAgZ3JvdXBfYnkoISFzeW0oYmlvbWFydF9maWx0ZXIpKSAlPiUKICBkcGx5cjo6ZmlsdGVyKHBhZGogPT0gbWluKHBhZGopKSAlPiUKICBkcGx5cjo6dW5ncm91cCgpICU+JQogIGRwbHlyOjpzZWxlY3QoISFzeW0oYmlvbWFydF9maWx0ZXIpLCBwYWRqKQoKc3VtbWFyeVRhYmxlIDwtIHN1bW1hcnlUYWJsZSAlPiUKICBsZWZ0X2pvaW4oaWRfdGFibGUsIGJ5PWJpb21hcnRfZmlsdGVyKSAlPiUKICBsZWZ0X2pvaW4obWF4RkNzLCBieT1iaW9tYXJ0X2ZpbHRlcikgJT4lCiAgbGVmdF9qb2luKG1pblB2YWxzLCBieT1iaW9tYXJ0X2ZpbHRlcikgJT4lCiAgZHBseXI6OnJlbmFtZShtYXhGb2xkQ2hhbmdlID0gbGluZWFyRm9sZENoYW5nZSwKICAgICAgICAgbWluRkRSX3B2YWwgPSBwYWRqKSAlPiUKICBkcGx5cjo6ZGlzdGluY3QoKSAlPiUKICBtdXRhdGUobWF4Rm9sZENoYW5nZT1hYnMobWF4Rm9sZENoYW5nZSkpICMgVGhpcyBlbGltaW5hdGVzIHRoZSBkaXJlY3Rpb24gb2YgY2hhbmdlOiB0aGlzIHdheSBpdCdzIGVhc3kgdG8gc29ydC4KCm51bUNvbHNUb1ByZXBlbmQgPC0gbmNvbChzdW1tYXJ5VGFibGUpIC0gMypsZW5ndGgocmVzTGlzdCkgLSAyICMgTnVtYmVyIG9mIGNvbHVtbnMgcGVyIGNvbnRyYXN0ID0gMy4gU3VidHJhY3QgdHdvIGZvciB0aGUgYmFzZU1lYW4gYW5kIGdlbmVzIGNvbHVtbnMuCmNvbFBvc2l0aW9uc1RvUHJlcGVuZFNUQVJUIDwtIG5jb2woc3VtbWFyeVRhYmxlKSAtIG51bUNvbHNUb1ByZXBlbmQgKyAxCmNvbFBvc2l0aW9uc09mRGF0YSA8LSBuY29sKHN1bW1hcnlUYWJsZSkgLSBudW1Db2xzVG9QcmVwZW5kCnN1bW1hcnlUYWJsZSA8LSBzdW1tYXJ5VGFibGVbLGMoMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xQb3NpdGlvbnNUb1ByZXBlbmRTVEFSVDpuY29sKHN1bW1hcnlUYWJsZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMjpjb2xQb3NpdGlvbnNPZkRhdGEpXQoKQ1BNZGRzREYgPC0gZGF0YS5mcmFtZShnZW5lcyA9IHJvdy5uYW1lcyhDUE1kZHMpLCBDUE1kZHMsIGNoZWNrLm5hbWVzPUYpCkNQTWRkc0RGIDwtIGRwbHlyOjpsZWZ0X2pvaW4oQ1BNZGRzREYsIGlkX3RhYmxlLCBieT1jKCJnZW5lcyIgPSBiaW9tYXJ0X2ZpbHRlcikpCm51bUNvbHNUb1ByZXBlbmQgPC0gbmNvbChDUE1kZHNERikgLSBuY29sKENQTWRkcykgLSAxCmNvbFBvc2l0aW9uc1RvUHJlcGVuZFNUQVJUIDwtIG5jb2woQ1BNZGRzREYpIC0gbnVtQ29sc1RvUHJlcGVuZCArIDEKY29sUG9zaXRpb25zT2ZEYXRhIDwtIG5jb2woQ1BNZGRzREYpIC0gbnVtQ29sc1RvUHJlcGVuZApDUE1kZHNERiA8LSBDUE1kZHNERlssYygxLGNvbFBvc2l0aW9uc1RvUHJlcGVuZFNUQVJUOm5jb2woQ1BNZGRzREYpLDI6Y29sUG9zaXRpb25zT2ZEYXRhKV0KYGBgCgpUaGUgY29kZSBhYm92ZSAobm90IHNob3duIGJ5IGRlZmF1bHQpIGdlbmVyYXRlcyB0YWJsZXMgc3VtbWFyaXppbmcgdGhlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyAoREVHcykuICBBIGxpbmVhciBmb2xkIGNoYW5nZSBjdXRvZmYgb2YgYHIgbGluZWFyX2ZjX2ZpbHRlcmAgYW5kIGFkanVzdGVkIHAtdmFsdWUgb2YgYHIgYWxwaGFgIHdhcyB1c2VkIHRvIGZpbHRlciB0aGUgcmVzdWx0cy4KCkhlcmUgaXMgdGhlIG51bWJlciBvZiBERUdzIGluIGVhY2ggZ3JvdXA6CgpgYGB7ciAnZGlzcGxheV9ERUdfc3VtbWFyeSd9CmthYmxlKGRlZ1RhYmxlLAogICAgICBjYXB0aW9uPSJOdW1iZXIgb2YgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIGFjcm9zcyBlYWNoIGNvbnRyYXN0IikgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSkKYGBgCgpgYGB7ciAnd3JpdGUtdGFibGVzJywgY29sbGFwc2U9VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXZhbD1mbGFnfQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyMjIFdyaXRlIHJlc3VsdHMgdGFibGUgZnJvbSBERVNlcTIKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnNldHdkKHBhdGhzJERFR19vdXRwdXQpCndyaXRlLnRhYmxlKGFsbFJlc3VsdHMsICAgICAgICAgZmlsZT1wYXN0ZTAoTk9STV9UWVBFLCItREVTZXFfb3V0cHV0X0FMTC50eHQiKSwgICAgICAgICAgICAgICBxdW90ZT1GLCBzZXA9J1x0JywgY29sLm5hbWVzPU5BKQp3cml0ZS50YWJsZShzaWduaWZpY2FudFJlc3VsdHMsIGZpbGU9cGFzdGUwKE5PUk1fVFlQRSwiLURFU2VxX291dHB1dF9zaWduaWZpY2FudC50eHQiKSwgICAgICAgcXVvdGU9Riwgc2VwPSdcdCcsIGNvbC5uYW1lcz1OQSkKd3JpdGUudGFibGUoc3VtbWFyeVRhYmxlLCAgICAgICBmaWxlPXBhc3RlMChOT1JNX1RZUEUsIi1ERVNlcV9vdXRwdXRfYWxsX2dlbmVzLnR4dCIpLCAgICAgICAgIHF1b3RlPUYsIHNlcD0nXHQnLCBjb2wubmFtZXM9TkEpCndyaXRlLnRhYmxlKENQTWRkc0RGLCAgICAgICAgICAgZmlsZT1wYXN0ZTAoTk9STV9UWVBFLCItUGVyX3NhbXBsZV9DUE0udHh0IiksICAgICAgICAgICAgICAgICBxdW90ZT1GLCBzZXA9J1x0JywgY29sLm5hbWVzPU5BKQp3cml0ZS50YWJsZShDb3VudHMsICAgICAgICAgICAgIGZpbGU9cGFzdGUwKE5PUk1fVFlQRSwiLVBlcl9zYW1wbGVfbm9ybWFsaXplZF9jb3VudHMudHh0IiksICAgcXVvdGU9Riwgc2VwPSdcdCcsIGNvbC5uYW1lcz1OQSkKYGBgCgpUaGUgY29kZSBhYm92ZSAobm90IHNob3duIGJ5IGRlZmF1bHQpIHdyaXRlcyB0ZXh0IGZpbGVzIGZvciBlYWNoIERFRyBzdW1tYXJ5IHR5cGUuCgojIFdyaXRlIGRhdGEKCmBgYHtyICd3cml0ZS1leGNlbC13b3Jrc2hlZXRzJywgY29sbGFwc2U9VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXZhbD1mbGFnfQpzZXR3ZChwYXRocyRERUdfb3V0cHV0KQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyMjIFdyaXRlIHJlc3VsdHMgYWJvdmUgYnV0IGluIEV4Y2VsCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyMgR2xvYmFsIG9wdGlvbnMKb3B0aW9ucygib3Blbnhsc3guYm9yZGVyQ29sb3VyIiA9ICIjNEY4MEJEIikKb3B0aW9ucygib3Blbnhsc3guYm9yZGVyU3R5bGUiID0gInRoaW4iKQpvcHRpb25zKCJvcGVueGxzeC5tYXhXaWR0aCIgPSA1MCkKaHMxIDwtIGNyZWF0ZVN0eWxlKHRleHREZWNvcmF0aW9uID0gIkJvbGQiLAogICAgICAgICAgICAgICAgICAgYm9yZGVyID0gIkJvdHRvbSIsCiAgICAgICAgICAgICAgICAgICBmb250Q29sb3VyID0gImJsYWNrIikKaHMyIDwtIGNyZWF0ZVN0eWxlKHRleHREZWNvcmF0aW9uID0gIkJvbGQiLAogICAgICAgICAgICAgICAgICAgYm9yZGVyID0gYygidG9wIiwgImJvdHRvbSIsICJsZWZ0IiwgInJpZ2h0IiksCiAgICAgICAgICAgICAgICAgICBmb250Q29sb3VyID0gImJsYWNrIiwKICAgICAgICAgICAgICAgICAgIGZnRmlsbD0iI0M1RDlGMSIpCgojIyMgU3VtbWFyeSByZXN1bHRzIC0gb25lIGdlbmUgcGVyIGxpbmUsIGNvbHVtbnMgYXJlIGNvbnRyYXN0cwp3YjEgPC0gY3JlYXRlV29ya2Jvb2soKQptb2RpZnlCYXNlRm9udCh3YjEsIGZvbnRTaXplID0gMTAsIGZvbnROYW1lID0gIkFyaWFsIE5hcnJvdyIpCmFkZFdvcmtzaGVldCh3YjEsICJERVNlcV9yZXN1bHRzX3Blcl9nZW5lIikKZm9yIChqIGluIDE6bGVuZ3RoKGNvbnRyYXN0c0luU3VtbWFyeSkpIHsKICBteVN0YXJ0Y29sPTcrKChqLTEpKjMpCiAgbXlFbmRjb2w9OSsoKGotMSkqMykKICBtZXJnZUNlbGxzKHdiMSwKICAgICAgICAgICAgIHNoZWV0ID0gMSwKICAgICAgICAgICAgIGNvbHMgPSBteVN0YXJ0Y29sOm15RW5kY29sLAogICAgICAgICAgICAgcm93cyA9IDEpCiAgd3JpdGVEYXRhKAogICAgd2IxLAogICAgc2hlZXQgPSAxLAogICAgeCA9IGNvbnRyYXN0c0luU3VtbWFyeVtqXSwKICAgIHN0YXJ0Q29sID0gbXlTdGFydGNvbCwKICAgIHN0YXJ0Um93ID0gMSkKfQpjb25kaXRpb25hbEZvcm1hdHRpbmcod2IxLAogICAgICAgICAgICAgICAgICAgICAgc2hlZXQgPSAxLAogICAgICAgICAgICAgICAgICAgICAgcm93cyA9IDEsCiAgICAgICAgICAgICAgICAgICAgICBjb2xzID0gMTpuY29sKHN1bW1hcnlUYWJsZSksCiAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gImNvbnRhaW5zIiwKICAgICAgICAgICAgICAgICAgICAgIHJ1bGUgPSAiIiwKICAgICAgICAgICAgICAgICAgICAgIHN0eWxlPWhzMikKZnJlZXplUGFuZSh3YjEsIHNoZWV0ID0gMSwgZmlyc3RBY3RpdmVSb3cgPSAzLCBmaXJzdEFjdGl2ZUNvbCA9IDQpCndyaXRlRGF0YVRhYmxlKHdiMSwKICAgICAgICAgICAgICAgc2hlZXQgPSAxLAogICAgICAgICAgICAgICBzdGFydFJvdyA9IDIsCiAgICAgICAgICAgICAgIHggPSBzdW1tYXJ5VGFibGUsCiAgICAgICAgICAgICAgIGNvbE5hbWVzID0gVFJVRSwKICAgICAgICAgICAgICAgcm93TmFtZXMgPSBGLAogICAgICAgICAgICAgICB0YWJsZVN0eWxlID0gIm5vbmUiLAogICAgICAgICAgICAgICBoZWFkZXJTdHlsZSA9IGhzMSwKICAgICAgICAgICAgICAga2VlcE5BID0gVCwKICAgICAgICAgICAgICAgbmEuc3RyaW5nID0gIk5BIikKc2V0Q29sV2lkdGhzKHdiMSwgc2hlZXQgPSAxLCBjb2xzID0gMTo2LCB3aWR0aHMgPSAiYXV0byIpICMgVGhpcyBpcyBoYXJkLWNvZGVkLCBzbyBwcm9uZSB0byBlcnJvcjsgd2lsbCBvbmx5IGltcGFjdCBhdXRvIGFkanVzdG1lbnQgb2YgY29sIHdpZHRocy4Kc2V0Q29sV2lkdGhzKHdiMSwgc2hlZXQgPSAxLCBjb2xzID0gNzpuY29sKHN1bW1hcnlUYWJsZSksIHdpZHRocyA9IDEzKSAjIFRoaXMgaXMgaGFyZC1jb2RlZCwgc28gcHJvbmUgdG8gZXJyb3I7IHdpbGwgb25seSBpbXBhY3QgYXV0byBhZGp1c3RtZW50IG9mIGNvbCB3aWR0aHMuCmZuYW1lMSA8LSBwYXN0ZTAoIjEuIixOT1JNX1RZUEUsIi1ERVNlcV9ieV9nZW5lLnhsc3giKQpzYXZlV29ya2Jvb2sod2IxLCBmbmFtZTEsIG92ZXJ3cml0ZSA9IFRSVUUpCgojIyMgQWxsIHJlc3VsdHMgaW4gb25lIHRhYmxlCndiMiA8LSBjcmVhdGVXb3JrYm9vaygpCm1vZGlmeUJhc2VGb250KHdiMiwgZm9udFNpemUgPSAxMCwgZm9udE5hbWUgPSAiQXJpYWwgTmFycm93IikKYWRkV29ya3NoZWV0KHdiMiwgcGFzdGUwKCJGRFIiLHBBZGpWYWx1ZSwiLkxpbmVhci5GQy4iLGxpbmVhcl9mY19maWx0ZXIpKQpmcmVlemVQYW5lKHdiMiwgc2hlZXQgPSAxLCBmaXJzdFJvdyA9IFRSVUUsIGZpcnN0QWN0aXZlQ29sID0gNCkKd3JpdGVEYXRhVGFibGUod2IyLAogICAgICAgICAgICAgICBzaGVldCA9IDEsCiAgICAgICAgICAgICAgIHggPSBzaWduaWZpY2FudFJlc3VsdHMsCiAgICAgICAgICAgICAgIGNvbE5hbWVzID0gVFJVRSwKICAgICAgICAgICAgICAgcm93TmFtZXMgPSBGLAogICAgICAgICAgICAgICB0YWJsZVN0eWxlID0gIm5vbmUiLAogICAgICAgICAgICAgICBoZWFkZXJTdHlsZSA9IGhzMSwKICAgICAgICAgICAgICAga2VlcE5BID0gVCwKICAgICAgICAgICAgICAgbmEuc3RyaW5nID0gIk5BIikKc2V0Q29sV2lkdGhzKHdiMiwgc2hlZXQgPSAxLCBjb2xzID0gMTpuY29sKHNpZ25pZmljYW50UmVzdWx0cyksIHdpZHRocyA9ICJhdXRvIikKYWRkV29ya3NoZWV0KHdiMiwgIkRFU2VxX2FsbF9yZXN1bHRzIikKZnJlZXplUGFuZSh3YjIsIHNoZWV0ID0gMiwgZmlyc3RSb3cgPSBUUlVFLCBmaXJzdEFjdGl2ZUNvbCA9IDQpCndyaXRlRGF0YVRhYmxlKHdiMiwKICAgICAgICAgICAgICAgc2hlZXQgPSAyLAogICAgICAgICAgICAgICB4ID0gYWxsUmVzdWx0cywKICAgICAgICAgICAgICAgY29sTmFtZXMgPSBUUlVFLAogICAgICAgICAgICAgICByb3dOYW1lcyA9IEYsCiAgICAgICAgICAgICAgIHRhYmxlU3R5bGUgPSAibm9uZSIsCiAgICAgICAgICAgICAgIGhlYWRlclN0eWxlID0gaHMxLAogICAgICAgICAgICAgICBrZWVwTkEgPSBULAogICAgICAgICAgICAgICBuYS5zdHJpbmcgPSAiTkEiKQpzZXRDb2xXaWR0aHMod2IyLCBzaGVldCA9IDIsIGNvbHMgPSAxOm5jb2woYWxsUmVzdWx0cyksIHdpZHRocyA9ICJhdXRvIikKc2F2ZVdvcmtib29rKHdiMiwgcGFzdGUwKCIyLiIsTk9STV9UWVBFLCItREVTZXFfYWxsLnhsc3giKSwgb3ZlcndyaXRlID0gVFJVRSkKCiMjIyBBbGwgcmVzdWx0cyB3aXRoIGRpZmZlcmVudCB0YWJzIGZvciBlYWNoIGNvbnRyYXN0CndiMyA8LSBjcmVhdGVXb3JrYm9vaygpCm1vZGlmeUJhc2VGb250KHdiMywgZm9udFNpemUgPSAxMCwgZm9udE5hbWUgPSAiQXJpYWwgTmFycm93IikKCnNob3J0X2NvbnRyYXN0X25hbWVzIDwtIHN0cmluZ3I6OnN0cl90cnVuYyhzaG9ydF9jb250cmFzdF9uYW1lcywgMzAsIHNpZGU9ImNlbnRlciIpCgpmb3IgKGkgaW4gMTpsZW5ndGgobGV2ZWxzKGZhY3RvcihhbGxSZXN1bHRzJGNvbnRyYXN0KSkpKSB7CiAgcHJpbnQoaSkKICBkYXRhVG9Xcml0ZSA8LSBhbGxSZXN1bHRzW2FsbFJlc3VsdHMkY29udHJhc3Q9PWxldmVscyhmYWN0b3IoYWxsUmVzdWx0cyRjb250cmFzdCkpW2ldLF0KICBhZGRXb3Jrc2hlZXQod2IzLCBzaG9ydF9jb250cmFzdF9uYW1lc1tpXSkKICBmcmVlemVQYW5lKHdiMywgc2hlZXQgPSBpLCBmaXJzdFJvdyA9IFRSVUUsIGZpcnN0QWN0aXZlQ29sID0gNCkKICB3cml0ZURhdGFUYWJsZSh3YjMsCiAgICAgICAgICAgICAgICAgc2hlZXQgPSBpLAogICAgICAgICAgICAgICAgIHggPSBkYXRhVG9Xcml0ZSwKICAgICAgICAgICAgICAgICBjb2xOYW1lcyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgcm93TmFtZXMgPSBGLAogICAgICAgICAgICAgICAgIHRhYmxlU3R5bGUgPSAibm9uZSIsCiAgICAgICAgICAgICAgICAgaGVhZGVyU3R5bGUgPSBoczEsCiAgICAgICAgICAgICAgICAga2VlcE5BID0gVCwKICAgICAgICAgICAgICAgICBuYS5zdHJpbmcgPSAiTkEiKQogIHNldENvbFdpZHRocyh3YjMsIHNoZWV0ID0gaSwgY29scyA9IDE6bmNvbChkYXRhVG9Xcml0ZSksIHdpZHRocyA9ICJhdXRvIikKfQpzYXZlV29ya2Jvb2sod2IzLCBwYXN0ZTAoIjMuIixOT1JNX1RZUEUsIi1ERVNlcV9ieV9jb250cmFzdC54bHN4IiksIG92ZXJ3cml0ZSA9IFRSVUUpCgojIyMgQ1BNCndiNCA8LSBjcmVhdGVXb3JrYm9vaygpCm1vZGlmeUJhc2VGb250KHdiNCwgZm9udFNpemUgPSAxMCwgZm9udE5hbWUgPSAiQXJpYWwgTmFycm93IikKYWRkV29ya3NoZWV0KHdiNCwgIkNvdW50cyBwZXIgbWlsbGlvbiIpCmZyZWV6ZVBhbmUod2I0LCBzaGVldCA9IDEsIGZpcnN0Um93ID0gVFJVRSwgZmlyc3RBY3RpdmVDb2wgPSA0KQp3cml0ZURhdGFUYWJsZSh3YjQsCiAgICAgICAgICAgICAgIHNoZWV0ID0gMSwKICAgICAgICAgICAgICAgeCA9IGFzLmRhdGEuZnJhbWUoQ1BNZGRzREYpLAogICAgICAgICAgICAgICBjb2xOYW1lcyA9IFRSVUUsCiAgICAgICAgICAgICAgIHJvd05hbWVzID0gRiwKICAgICAgICAgICAgICAgdGFibGVTdHlsZSA9ICJub25lIiwKICAgICAgICAgICAgICAgaGVhZGVyU3R5bGUgPSBoczEsCiAgICAgICAgICAgICAgIGtlZXBOQSA9IFQsCiAgICAgICAgICAgICAgIG5hLnN0cmluZyA9ICJOQSIpCnNldENvbFdpZHRocyh3YjQsIHNoZWV0ID0gMSwgY29scyA9IDE6bmNvbChDUE1kZHNERiksIHdpZHRocyA9ICJhdXRvIikKc2F2ZVdvcmtib29rKHdiNCwgcGFzdGUwKCI0LiIsTk9STV9UWVBFLCItQ1BNLnhsc3giKSwgb3ZlcndyaXRlID0gVFJVRSkKCiMjIyBJUEEKd2I1IDwtIGNyZWF0ZVdvcmtib29rKCkKbW9kaWZ5QmFzZUZvbnQod2I1LCBmb250U2l6ZSA9IDEwLCBmb250TmFtZSA9ICJBcmlhbCBOYXJyb3ciKQphZGRXb3Jrc2hlZXQod2I1LCAiRm9yIElQQSB1cGxvYWQiKQpmcmVlemVQYW5lKHdiNSwgc2hlZXQgPSAxLCBmaXJzdFJvdyA9IFRSVUUsIGZpcnN0QWN0aXZlQ29sID0gNSkKd3JpdGVEYXRhVGFibGUod2I1LAogICAgICAgICAgICAgICBzaGVldCA9IDEsCiAgICAgICAgICAgICAgIHggPSBhcy5kYXRhLmZyYW1lKElQQSksCiAgICAgICAgICAgICAgIGNvbE5hbWVzID0gVFJVRSwKICAgICAgICAgICAgICAgcm93TmFtZXMgPSBGLAogICAgICAgICAgICAgICB0YWJsZVN0eWxlID0gIm5vbmUiLAogICAgICAgICAgICAgICBoZWFkZXJTdHlsZSA9IGhzMSwKICAgICAgICAgICAgICAga2VlcE5BID0gVCwKICAgICAgICAgICAgICAgbmEuc3RyaW5nID0gIk5BIikKc2V0Q29sV2lkdGhzKHdiNSwgc2hlZXQgPSAxLCBjb2xzID0gMTpuY29sKElQQSksIHdpZHRocyA9ICJhdXRvIikKc2F2ZVdvcmtib29rKHdiNSwgcGFzdGUwKCI1LiIsTk9STV9UWVBFLCItSVBBLnhsc3giKSwgb3ZlcndyaXRlID0gVFJVRSkKCmBgYAoKVGhlIGNvZGUgYWJvdmUgKG5vdCBzaG93biBieSBkZWZhdWx0KSB3cml0ZXMgRXhjZWwgd29ya2Jvb2tzIGFuZCB0ZXh0IGZpbGVzIG9mIERFRyBsaXN0cy4KClRoZXNlIGZpbGVzIHNob3VsZCBiZSBwcm92aWRlZCB0byB5b3UgYXMgYSBzZXBhcmF0ZSB6aXAgZmlsZS4KCmBgYHtyICdlbWJlZF9leGNlbF9maWxlcycsIGV2YWw9ZW1iZWRGaWxlc30Kc2V0d2QocGF0aHMkREVHX291dHB1dCkKeGZ1bjo6ZW1iZWRfZmlsZXMobGlzdC5maWxlcygiLiIsICJbLl0oeGxzeCkkIiksIG5hbWUgPSAiUk5BU2VxX1NwcmVhZHNoZWV0cy56aXAiKQpgYGAKCmBgYHtyICdlbWJlZF90ZXh0X2ZpbGVzJywgZXZhbD1lbWJlZEZpbGVzfQpzZXR3ZChwYXRocyRERUdfb3V0cHV0KQp4ZnVuOjplbWJlZF9maWxlcyhsaXN0LmZpbGVzKCIuIiwgIlsuXSh0eHQpJCIpLCBuYW1lID0gIlJOQVNlcV9UZXh0X0ZpbGVzLnppcCIpCmBgYAoKYGBge3IgJ2VtYmVkX09EQUZfZmlsZXMnLCBldmFsPWVtYmVkRmlsZXN9CnNldHdkKHBhdGhzJERFR19vdXRwdXQpCnhmdW46OmVtYmVkX2RpcihPREFGZGlyLCBuYW1lID0gIlJOQVNlcV9SLU9EQUZfdGV4dF9maWxlcy56aXAiKQpgYGAKCmBgYHtyICdzYXZlX3BvaW50Jywgc2hvdz1GQUxTRSwgZXZhbD1mbGFnfQojIFNhdmUgUkRhdGEgaGVyZSBpZiBmbGFnIGlzIFRSVUUuCmlmKGlzLm5hKHBhcmFtcyRncm91cF9mYWNldCkpIHsKICBzYXZlLmltYWdlKGZpbGUgPSBmaWxlLnBhdGgocGF0aHMkUkRhdGEsICJQYXJ0aWFsX2FuYWx5c2lzLlJEYXRhIikpCn0gZWxzZSB7CiAgc2F2ZS5pbWFnZShmaWxlID0gZmlsZS5wYXRoKHBhdGhzJFJEYXRhLCBwYXN0ZTAoIlBhcnRpYWxfYW5hbHlzaXNfIiwgcGFyYW1zJGdyb3VwX2ZpbHRlciwgIi5SRGF0YSIpKSkKfQojIEFkZCBhIGZsYWcgdG8gdGhlIHN0YXJ0IG9mIHRoZSBzY3JpcHQgd2l0aCBpZiBzdGF0ZW1lbnRzIGluIGFsbCBjb2RlIGNodW5rcyB0byBjaGVjayBpZiB0aGUgZmxhZyBpcyBzZXQuCiMgSWYgZmxhZyBpcyBUUlVFLCBydW4gYW5hbHlzaXM7IGlmIGZsYWcgaXMgRkFMU0UsIFNLSVAgYW5hbHlzaXMgYW5kIGNvbnRpbnVlIGhlcmUgKGp1c3QgdG8gbW9kaWZ5IHBsb3R0aW5nIG91dHB1dCkuCmBgYAoKIyBQQ0EgUGxvdHMgey50YWJzZXQgLnRhYnNldC1mYWRlfQoKIyMgQWxsIGdlbmVzLCBiZWZvcmUgZmlsdGVyaW5nCgpgYGB7ciAnUENBJ30KIyMgUGVyZm9ybSBQQ0EgYW5hbHlzaXMgYW5kIG1ha2UgcGxvdApwbG90UENBKHJsZCwgaW50Z3JvdXAgPSBwYXJhbXMkaW50Z3JvdXAsIG50b3A9bnJvdyhhc3NheShybGQpKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChuY29sPTMpKQojIyBHZXQgcGVyY2VudCBvZiB2YXJpYW5jZSBleHBsYWluZWQKZGF0YV9wY2EgPC0gcGxvdFBDQShybGQsIGludGdyb3VwID0gcGFyYW1zJGludGdyb3VwLCBudG9wPW5yb3coYXNzYXkocmxkKSksIHJldHVybkRhdGEgPSBUUlVFKQpwZXJjZW50VmFyIDwtIHJvdW5kKDEwMCAqIGF0dHIoZGF0YV9wY2EsICJwZXJjZW50VmFyIikpCgpgYGAKClRoaXMgcGxvdCBzaG93cyB0aGUgZmlyc3QgdHdvIHByaW5jaXBhbCBjb21wb25lbnRzIHRoYXQgZXhwbGFpbiB0aGUgdmFyaWFiaWxpdHkgaW4gdGhlIGRhdGEgdXNpbmcgdGhlIHJlZ3VsYXJpemVkIGxvZyBjb3VudCBkYXRhLiBJZiB5b3UgYXJlIHVuZmFtaWxpYXIgd2l0aCBwcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzLCB5b3UgbWlnaHQgd2FudCB0byBjaGVjayB0aGUgW1dpa2lwZWRpYSBlbnRyeV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUHJpbmNpcGFsX2NvbXBvbmVudF9hbmFseXNpcykgb3IgdGhpcyBbaW50ZXJhY3RpdmUgZXhwbGFuYXRpb25dKGh0dHA6Ly9zZXRvc2EuaW8vZXYvcHJpbmNpcGFsLWNvbXBvbmVudC1hbmFseXNpcy8pLiBJbiB0aGlzIGNhc2UsIHRoZSBmaXJzdCBhbmQgc2Vjb25kIHByaW5jaXBhbCBjb21wb25lbnQgZXhwbGFpbiBgciBwZXJjZW50VmFyWzFdYCBhbmQgYHIgcGVyY2VudFZhclsyXWAgcGVyY2VudCBvZiB0aGUgdmFyaWFuY2UgcmVzcGVjdGl2ZWx5LgoKIyMgREVHcyBvbmx5CgpgYGB7ciAnUENBX0RFR3MnfQpybGRfZGVncyA8LSBybGRbcm93Lm5hbWVzKGFzc2F5KHJsZCkpICVpbiUgc2lnbmlmaWNhbnRSZXN1bHRzW1tiaW9tYXJ0X2ZpbHRlcl1dLF0gICMgZFsgZFtbVEhFQ09MVU1OXV0gPT0gc29tZVZhbHVlICwgXQoKIyMgUGVyZm9ybSBQQ0EgYW5hbHlzaXMgYW5kIG1ha2UgcGxvdApwbG90UENBKHJsZF9kZWdzLCBpbnRncm91cCA9IHBhcmFtcyRpbnRncm91cCwgbnRvcD1ucm93KHJsZF9kZWdzKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5jb2w9MykpClBDQXBsb3RERUdzIDwtIHBsb3RQQ0EocmxkX2RlZ3MsIGludGdyb3VwID0gcGFyYW1zJGludGdyb3VwLCBudG9wPW5yb3cocmxkX2RlZ3MpLCByZXR1cm5EYXRhID0gVFJVRSkKCiMjIEdldCBwZXJjZW50IG9mIHZhcmlhbmNlIGV4cGxhaW5lZApwZXJjZW50VmFyREVHcyA8LSByb3VuZCgxMDAgKiBhdHRyKFBDQXBsb3RERUdzLCAicGVyY2VudFZhciIpKQpgYGAKClRoaXMgcGxvdCBzaG93cyB0aGUgcHJpbmNpcGFsIGNvbXBvbmVudHMgYW5hbHlzaXMgbGltaXRlZCB0byBhbGwgREVHcy4gSW4gdGhpcyBjYXNlLCB0aGUgZmlyc3QgYW5kIHNlY29uZCBwcmluY2lwYWwgY29tcG9uZW50IGV4cGxhaW4gYHIgcGVyY2VudFZhckRFR3NbMV1gIGFuZCBgciBwZXJjZW50VmFyREVHc1syXWAgcGVyY2VudCBvZiB0aGUgdmFyaWFuY2UgcmVzcGVjdGl2ZWx5LgoKIyMgVG9wIGByIG5CZXN0YCBtb3N0IHZhcmlhYmxlIGdlbmVzIG9ubHkKCmBgYHtyICdQQ0FfdG9wJ30KCiMgUnVuIHRoaXMgY29kZSBvbmx5IG9uY2UgZm9yIGJvdGggdGhlIFBDQSBhbmQgY2x1c3RlcmluZyBhbmFseXNpcwpydiA9IHJvd1ZhcnMoYXNzYXkocmxkKSkKc2VsZWN0ID0gb3JkZXIocnYsIGRlY3JlYXNpbmcgPSBUUlVFKVsxOm5CZXN0XQoKcmxkX3RvcCA8LSBybGRbc2VsZWN0LF0KCiMjIFBlcmZvcm0gUENBIGFuYWx5c2lzIGFuZCBtYWtlIHBsb3QKcGxvdFBDQShybGRfdG9wLCBpbnRncm91cCA9IHBhcmFtcyRpbnRncm91cCwgbnRvcD1ucm93KHJsZF90b3ApKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5jb2w9MykpCgojIyBHZXQgcGVyY2VudCBvZiB2YXJpYW5jZSBleHBsYWluZWQKZGF0YV9wY2FUb3AgPC0gcGxvdFBDQShybGRfdG9wLCBpbnRncm91cCA9IHBhcmFtcyRpbnRncm91cCwgcmV0dXJuRGF0YSA9IFRSVUUsIG50b3A9bnJvdyhybGRfdG9wKSkKcGVyY2VudFZhclRvcCA8LSByb3VuZCgxMDAgKiBhdHRyKGRhdGFfcGNhVG9wLCAicGVyY2VudFZhciIpKQpgYGAKClRoaXMgcGxvdCBzaG93cyB0aGUgcHJpbmNpcGFsIGNvbXBvbmVudHMgYW5hbHlzaXMgbGltaXRlZCB0byBhbGwgREVHcy4gSW4gdGhpcyBjYXNlLCB0aGUgZmlyc3QgYW5kIHNlY29uZCBwcmluY2lwYWwgY29tcG9uZW50IGV4cGxhaW4gYHIgcGVyY2VudFZhclRvcFsxXWAgYW5kIGByIHBlcmNlbnRWYXJUb3BbMl1gIHBlcmNlbnQgb2YgdGhlIHZhcmlhbmNlIHJlc3BlY3RpdmVseS4KCiMgU2FtcGxlLXRvLXNhbXBsZSBkaXN0YW5jZXMgey50YWJzZXQgLnRhYnNldC1mYWRlfQoKIyMgQWxsIGdlbmVzLCBiZWZvcmUgZmlsdGVyaW5nCgpgYGB7ciAnc2FtcGxlRGlzdCcsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0KIyMgT2J0YWluIHRoZSBzYW1wbGUgZXVjbGlkZWFuIGRpc3RhbmNlcwpzYW1wbGVEaXN0cyA8LSBzdGF0czo6ZGlzdCh0KGFzc2F5KHJsZCkpKQpzYW1wbGVEaXN0TWF0cml4IDwtIGFzLm1hdHJpeChzYW1wbGVEaXN0cykKIyMgQWRkIG5hbWVzIGJhc2VkIG9uIGludGdyb3VwCnJvd25hbWVzKHNhbXBsZURpc3RNYXRyaXgpIDwtIGFwcGx5KGFzLmRhdGEuZnJhbWUoY29sRGF0YShybGQpWywgcGFyYW1zJGRlc2lnbl0pLCAxLAogICAgcGFzdGUsIGNvbGxhcHNlID0gJyA6ICcpCmNvbG5hbWVzKHNhbXBsZURpc3RNYXRyaXgpIDwtIE5VTEwKCiMjIERlZmluZSBjb2xvcnMgdG8gdXNlIGZvciB0aGUgaGVhdG1hcApjb2xvcnMgPC0gY29sb3JSYW1wUGFsZXR0ZSggcmV2KGJyZXdlci5wYWwoOSwgIkJsdWVzIikpICkoMjU1KQoKIyMgTWFrZSB0aGUgaGVhdG1hcApwaGVhdG1hcChzYW1wbGVEaXN0TWF0cml4LCBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSBzYW1wbGVEaXN0cywKICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9IHNhbXBsZURpc3RzLCBjb2xvciA9IGNvbG9ycykKCmBgYAoKVGhpcyBwbG90IHNob3dzIGhvdyBzYW1wbGVzIGFyZSBjbHVzdGVyZWQgYmFzZWQgb24gdGhlaXIgZXVjbGlkZWFuIGRpc3RhbmNlIHVzaW5nIHRoZSByZWd1bGFyaXplZCBsb2cgdHJhbnNmb3JtZWQgY291bnQgZGF0YS4gVGhpcyBmaWd1cmUgZ2l2ZXMgYW4gb3ZlcnZpZXcgb2YgaG93IHRoZSBzYW1wbGVzIGFyZSBoaWVyYXJjaGljYWxseSBjbHVzdGVyZWQuIEl0IGlzIGEgY29tcGxlbWVudGFyeSBmaWd1cmUgdG8gdGhlIFBDQSBwbG90LgoKIyMgREVHcyBvbmx5CgpgYGB7ciAnc2FtcGxlRGlzdF9kZWdzJywgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQojIyBMaW1pdCB0byB0aGUgdG9wIG4gZ2VuZXMKIyBydiA9IHJvd1ZhcnMoYXNzYXkocmxkKSkKIyBzZWxlY3QgPSBvcmRlcihydiwgZGVjcmVhc2luZyA9IFRSVUUpWzE6bkJlc3RdCiMjIE9idGFpbiB0aGUgc2FtcGxlIGV1Y2xpZGVhbiBkaXN0YW5jZXMKIyBzYW1wbGVEaXN0c1RvcCA8LSBkaXN0KHQoYXNzYXkocmxkKVtzZWxlY3QsXSkpCnNhbXBsZURpc3RzREVHcyA8LSBzdGF0czo6ZGlzdCh0KGFzc2F5KHJsZF9kZWdzKSkpCiMgc2FtcGxlRGlzdE1hdHJpeFRvcCA8LSBhcy5tYXRyaXgoc2FtcGxlRGlzdHNUb3ApCnNhbXBsZURpc3RNYXRyaXhERUdzIDwtIGFzLm1hdHJpeChzYW1wbGVEaXN0c0RFR3MpCgojIyBBZGQgbmFtZXMgYmFzZWQgb24gaW50Z3JvdXAKcm93bmFtZXMoc2FtcGxlRGlzdE1hdHJpeERFR3MpIDwtIGFwcGx5KGFzLmRhdGEuZnJhbWUoY29sRGF0YShybGQpWywgcGFyYW1zJGRlc2lnbl0pLCAxLAogICAgcGFzdGUsIGNvbGxhcHNlID0gJyA6ICcpCmNvbG5hbWVzKHNhbXBsZURpc3RNYXRyaXhERUdzKSA8LSBOVUxMCgojIyBEZWZpbmUgY29sb3JzIHRvIHVzZSBmb3IgdGhlIGhlYXRtYXAKY29sb3JzIDwtIGNvbG9yUmFtcFBhbGV0dGUoIHJldihicmV3ZXIucGFsKDksICJCbHVlcyIpKSApKDI1NSkKCiMjIE1ha2UgdGhlIGhlYXRtYXAKcGhlYXRtYXAoc2FtcGxlRGlzdE1hdHJpeERFR3MsCiAgICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9IHNhbXBsZURpc3RzREVHcywKICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gc2FtcGxlRGlzdHNERUdzLAogICAgICAgICBjb2xvciA9IGNvbG9ycykKYGBgCgpUaGlzIHBsb3Qgc2hvd3MgaG93IHNhbXBsZXMgYXJlIGNsdXN0ZXJlZCBiYXNlZCBvbiB0aGVpciBldWNsaWRlYW4gZGlzdGFuY2UgdXNpbmcgdGhlIHJlZ3VsYXJpemVkIGxvZyB0cmFuc2Zvcm1lZCBjb3VudCBkYXRhIG9mIGFsbCBERUdzLiBUaGlzIGZpZ3VyZSBnaXZlcyBhbiBvdmVydmlldyBvZiBob3cgdGhlIHNhbXBsZXMgYXJlIGhpZXJhcmNoaWNhbGx5IGNsdXN0ZXJlZC4gSXQgaXMgYSBjb21wbGVtZW50YXJ5IGZpZ3VyZSB0byB0aGUgUENBIHBsb3QuCgojIyBNb3N0IHZhcmlhYmxlIGdlbmVzIG9ubHkKCmBgYHtyICdzYW1wbGVEaXN0X3RvcCcsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0KIyMgTGltaXQgdG8gdGhlIHRvcCBuIGdlbmVzCiMjIFVzaW5nIHRoZSBzZWxlY3Qgb2JqZWN0IGZyb20gUENBIGNvZGUgYWJvdmUuLi4KIyMgT2J0YWluIHRoZSBzYW1wbGUgZXVjbGlkZWFuIGRpc3RhbmNlcwpzYW1wbGVEaXN0c1RvcCA8LSBzdGF0czo6ZGlzdCh0KGFzc2F5KHJsZClbc2VsZWN0LF0pKQpzYW1wbGVEaXN0TWF0cml4VG9wIDwtIGFzLm1hdHJpeChzYW1wbGVEaXN0c1RvcCkKCiMjIEFkZCBuYW1lcyBiYXNlZCBvbiBpbnRncm91cApyb3duYW1lcyhzYW1wbGVEaXN0TWF0cml4VG9wKSA8LSBhcHBseShhcy5kYXRhLmZyYW1lKGNvbERhdGEocmxkKVssIHBhcmFtcyRkZXNpZ25dKSwgMSwKICAgIHBhc3RlLCBjb2xsYXBzZSA9ICcgOiAnKQpjb2xuYW1lcyhzYW1wbGVEaXN0TWF0cml4VG9wKSA8LSBOVUxMCgojIyBEZWZpbmUgY29sb3JzIHRvIHVzZSBmb3IgdGhlIGhlYXRtYXAKY29sb3JzIDwtIGNvbG9yUmFtcFBhbGV0dGUoIHJldihicmV3ZXIucGFsKDksICJCbHVlcyIpKSApKDI1NSkKCiMjIE1ha2UgdGhlIGhlYXRtYXAKcGhlYXRtYXAoc2FtcGxlRGlzdE1hdHJpeFRvcCwKICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gc2FtcGxlRGlzdHNUb3AsCiAgICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9IHNhbXBsZURpc3RzVG9wLAogICAgICAgICBjb2xvciA9IGNvbG9ycykKYGBgCgpUaGlzIHBsb3Qgc2hvd3MgaG93IHNhbXBsZXMgYXJlIGNsdXN0ZXJlZCBiYXNlZCBvbiB0aGVpciBldWNsaWRlYW4gZGlzdGFuY2UgdXNpbmcgdGhlIHJlZ3VsYXJpemVkIGxvZyB0cmFuc2Zvcm1lZCBjb3VudCBkYXRhIG9mIHRoZSB0b3AgYHIgbkJlc3RgIG1vc3QgdmFyaWFibGUgZ2VuZXMuIFRoaXMgZmlndXJlIGdpdmVzIGFuIG92ZXJ2aWV3IG9mIGhvdyB0aGUgc2FtcGxlcyBhcmUgaGllcmFyY2hpY2FsbHkgY2x1c3RlcmVkLiBJdCBpcyBhIGNvbXBsZW1lbnRhcnkgZmlndXJlIHRvIHRoZSBQQ0EgcGxvdC4KCiMgSGVhdG1hcHMgey50YWJzZXQgLnRhYnNldC1mYWRlfQoKIyMgQWxsIERFR3MKCmBgYHtyICdoZWF0bWFwX2FsbF9ERUdzJywgY29sbGFwc2U9VFJVRSwgZmlnLmhlaWdodD0xMH0KCm1hdCA8LSBhc3NheShybGQpW3Jvdy5uYW1lcyhhc3NheShybGQpKSAlaW4lIHNpZ25pZmljYW50UmVzdWx0c1tbYmlvbWFydF9maWx0ZXJdXSxdCm1hdCA8LSBtYXQgLSByb3dNZWFucyhtYXQpCgpoZWF0bWFwX2RmIDwtIGFzLmRhdGEuZnJhbWUoY29sRGF0YShybGQpWyxwYXJhbXMkaW50Z3JvdXBdKQpyb3cubmFtZXMoaGVhdG1hcF9kZikgPC0gY29sRGF0YShybGQpJG9yaWdpbmFsX25hbWVzICMgQ3VzdG9taXplIQpwaGVhdG1hcChtYXQsCiAgICAgICAgIGFubm90YXRpb25fY29sPWhlYXRtYXBfZGYsCiAgICAgICAgIHNob3dfcm93bmFtZXMgPSBGQUxTRSwKICAgICAgICAgYm9yZGVyX2NvbG9yID0gTkEsCiAgICAgICAgIHNjYWxlPSJyb3ciKQojIGNvbG9yID0gaW5mZXJubygxMCkKCmBgYAoKIyMgVG9wIGByIG5IZWF0bWFwREVHc2AgZGlmZmVyZW50aWFsbHkgYWJ1bmRhbnQgZ2VuZXMKCmBgYHtyICdoZWF0bWFwX3RvcF9kZWdzJywgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwLCBjb2xsYXBzZT1UUlVFfQptYXRfdG9wIDwtIGFzc2F5KHJsZClbcm93Lm5hbWVzKGFzc2F5KHJsZCkpICVpbiUgYWxsUmVzdWx0c09yZGVyZWRfbG9nRkNfZmlsdGVyWzE6bkhlYXRtYXBERUdzLF1bW2Jpb21hcnRfZmlsdGVyXV0sXQptYXRfdG9wIDwtIG1hdF90b3AgLSByb3dNZWFucyhtYXRfdG9wKQoKZ2VuZXNfZm9yX2hlYXRtYXAgPC0gcm93Lm5hbWVzKG1hdF90b3ApCmdlbmVzX2Zvcl9oZWF0bWFwIDwtIGRhdGEuZnJhbWUoZ2VuZXM9cm93Lm5hbWVzKG1hdF90b3ApKQpuYW1lcyhnZW5lc19mb3JfaGVhdG1hcClbbmFtZXMoZ2VuZXNfZm9yX2hlYXRtYXApID09ICJnZW5lcyJdIDwtIGJpb21hcnRfZmlsdGVyCmdlbmVzX2Zvcl9oZWF0bWFwIDwtIGRwbHlyOjpsZWZ0X2pvaW4oZ2VuZXNfZm9yX2hlYXRtYXAsIGlkX3RhYmxlX2VudHJleiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5PWJpb21hcnRfZmlsdGVyKSAlPiUKICBkcGx5cjo6ZGlzdGluY3QoKQoKcGhlYXRtYXAobWF0X3RvcCwKICAgICAgICAgYW5ub3RhdGlvbl9jb2w9aGVhdG1hcF9kZiwKICAgICAgICAgbGFiZWxzX3Jvdz1nZW5lc19mb3JfaGVhdG1hcFtbImV4dGVybmFsX2dlbmVfbmFtZSJdXSwKICAgICAgICAgc2hvd19yb3duYW1lcyA9IFRSVUUsCiAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLAogICAgICAgICBzY2FsZT0icm93IikKIyBjb2xvciA9IGluZmVybm8oMTApCgpgYGAKCiMjIFRvcCBgciBuSGVhdG1hcGAgdmFyaWFibGUgZ2VuZXMKCmBgYHtyICdoZWF0bWFwX3RvcF9uX2dlbmVzJywgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwLCBjb2xsYXBzZT1UUlVFfQpydiA9IHJvd1ZhcnMoYXNzYXkocmxkKSkKc2VsZWN0ID0gb3JkZXIocnYsIGRlY3JlYXNpbmcgPSBUUlVFKVsxOm5IZWF0bWFwXQptYXRSViA8LSBhc3NheShybGQpW3NlbGVjdCxdCm1hdFJWIDwtIG1hdFJWIC0gcm93TWVhbnMobWF0UlYpCgpnZW5lc19mb3JfaGVhdG1hcCA8LSByb3cubmFtZXMobWF0UlYpCmdlbmVzX2Zvcl9oZWF0bWFwIDwtIGRhdGEuZnJhbWUoZ2VuZXM9cm93Lm5hbWVzKG1hdFJWKSkKbmFtZXMoZ2VuZXNfZm9yX2hlYXRtYXApW25hbWVzKGdlbmVzX2Zvcl9oZWF0bWFwKSA9PSAiZ2VuZXMiXSA8LSBiaW9tYXJ0X2ZpbHRlcgpnZW5lc19mb3JfaGVhdG1hcCA8LSBkcGx5cjo6bGVmdF9qb2luKGdlbmVzX2Zvcl9oZWF0bWFwLCBpZF90YWJsZV9lbnRyZXosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnk9YmlvbWFydF9maWx0ZXIpICU+JQogIGRwbHlyOjpkaXN0aW5jdCgpCgpwaGVhdG1hcChtYXRSViwKICAgICAgICAgI2NvbG9yID0gcmV2KGJyZXdlci5wYWwoMTEsIlJkQnUiKSksICMgaW5mZXJubygxMCksCiAgICAgICAgIGFubm90YXRpb25fY29sPWhlYXRtYXBfZGYsCiAgICAgICAgIGxhYmVsc19yb3c9Z2VuZXNfZm9yX2hlYXRtYXBbWyJleHRlcm5hbF9nZW5lX25hbWUiXV0sCiAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLAogICAgICAgICBzY2FsZSA9ICJyb3ciLAogICAgICAgICBjdXRyZWVfcm93cyA9IDMsCiAgICAgICAgIGN1dHJlZV9jb2xzID0gNCkKCmBgYAoKCiMgTUEgcGxvdHMKClRoaXMgc2VjdGlvbiBjb250YWlucyB0aHJlZSBncm91cHMgb2YgTUEgcGxvdHMgKHNlZSBbV2lraXBlZGlhXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9NQV9wbG90KSkgdGhhdCBjb21wYXJlIHRoZSBtZWFuIG9mIHRoZSBub3JtYWxpemVkIGNvdW50cyBhZ2FpbnN0IHRoZSBsb2cgZm9sZCBjaGFuZ2UuIEVhY2ggb2YgdGhlIGdyb3VwcyBoYXMgYSB0YWIgZm9yIGVhY2ggY29udHJhc3QuIFRoZSBwbG90cyBzaG93IG9uZSBwb2ludCBwZXIgZmVhdHVyZS4gVGhlIHBvaW50cyBhcmUgc2hvd24gaW4gcmVkIGlmIHRoZSBmZWF0dXJlIGhhcyBhbiBhZGp1c3RlZCBwLXZhbHVlIGxlc3MgdGhhbiB0aGUgY3V0b2ZmIGxpc3RlZCBpbiBlYWNoIHNlY3Rpb24sIHRoYXQgaXMsIHRoZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGZlYXR1cmVzIGFyZSBzaG93biBpbiByZWQuCgojIyBGaWx0ZXJlZCBhdCBgciBtZXRhZGF0YShyZXNMaXN0W1sxXV0pJGFscGhhYCB7LnRhYnNldCAudGFic2V0LWZhZGV9CgpUaGlzIGdyb3VwIG9mIHBsb3RzIHNob3dzIGBhbHBoYWAgPSBgciBtZXRhZGF0YShyZXNMaXN0W1sxXV0pJGFscGhhYCwgd2hpY2ggaXMgdGhlIGBhbHBoYWAgdmFsdWUgdXNlZCB0byBkZXRlcm1pbmUgd2hpY2ggcmVzdWx0aW5nIGZlYXR1cmVzIHdlcmUgc2lnbmlmaWNhbnQgd2hlbiBydW5uaW5nIHRoZSBmdW5jdGlvbiBgREVTZXEyOjpyZXN1bHRzKClgLiAgCgoKYGBge3IgJ01BcGxvdGFscGhhJywgcmVzdWx0cz0nYXNpcyd9CiMjIE1BIHBsb3Qgd2l0aCBhbHBoYSB1c2VkIGluIERFU2VxMjo6cmVzdWx0cygpCmZvciAoaSBpbiBzZXFfYWxvbmcocmVzTGlzdCkpIHsKICBjb250cmFzdCA9IGdzdWIocGF0dGVybiA9IHBhc3RlMCgibG9nMi4qIiwgREVTSUdOLCAiXCAiKSwKICAgICAgICAgICAgICAgICAgcmVwbGFjZW1lbnQgPSAgIiIsCiAgICAgICAgICAgICAgICAgIHggPSByZXNMaXN0W1tpXV1AZWxlbWVudE1ldGFkYXRhW1syXV1bMl0pCiAgY2F0KCIjIyMiLCBjb250cmFzdCwgIiAgXG5cbiIpCiAgTUFfdGl0bGUgPC0gcGFzdGUwKCdNQSBwbG90LCBhbHBoYSA9ICcsIG1ldGFkYXRhKHJlc0xpc3RbW2ldXSkkYWxwaGEsJywgJyxjb250cmFzdCkKICBERVNlcTI6OnBsb3RNQShyZXNMaXN0W1tpXV0sIGFscGhhID0gbWV0YWRhdGEocmVzTGlzdFtbaV1dKSRhbHBoYSwgbWFpbiA9IHN0cl93cmFwKE1BX3RpdGxlLCB3aWR0aD00MCkpCiAgY2F0KCcgIFxuXG4nKQp9CgojIyBVc2VkIHRvIGhhdmUgbXVsdGlwbGUgdGFic2V0cyBmb3IgTUEgcGxvdHMuLi4gaXQgd2FzIHRvbyBtYW55IE1BIHBsb3RzLgoKIyMgTUEgcGxvdCB3aXRoIGFscGhhID0gMS8yIG9mIHRoZSBhbHBoYSB1c2VkIGluIERFU2VxMjo6cmVzdWx0cygpCiMgZm9yIChpIGluIDE6bGVuZ3RoKHJlc0xpc3QpKSB7CiMgY29udHJhc3QgPSBnc3ViKHBhdHRlcm4gPSAibG9nMi4qVGltZVwgIiwKIyAgICAgICAgICAgICAgICAgcmVwbGFjZW1lbnQgPSAgIiIsCiMgICAgICAgICAgICAgICAgIHggPSByZXNMaXN0W1tpXV1AZWxlbWVudE1ldGFkYXRhW1syXV1bMl0pCiMgY2F0KCIjIyMiLCBjb250cmFzdCwgIiBcbiIpCiMgREVTZXEyOjpwbG90TUEocmVzTGlzdFtbaV1dLCBhbHBoYSA9IG1ldGFkYXRhKHJlc0xpc3RbW2ldXSkkYWxwaGEgLyAyLAojICAgICBtYWluID0gcGFzdGUoJ01BIHBsb3Qgd2l0aCBhbHBoYSA9JywgbWV0YWRhdGEocmVzTGlzdFtbaV1dKSRhbHBoYSAvIDIsJywnLGNvbnRyYXN0KSkKIyBjYXQoJ1xuXG4nKQojIH0KCiMjIE1BIHBsb3Qgd2l0aCBhbHBoYSBjb3JyZXNwb25kaW5nIHRvIHRoZSBvbmUgdGhhdCBnaXZlcyB0aGUgbkJlc3QgZmVhdHVyZXMKIyBmb3IgKGkgaW4gMTpsZW5ndGgocmVzTGlzdCkpIHsKIyBuQmVzdC5hY3R1YWwgPC0gbWluKG5CZXN0LCBucm93KGhlYWQocmVzTGlzdFtbaV1dLCBuID0gbkJlc3QpKSkKIyBuQmVzdC5hbHBoYSA8LSBoZWFkKCByZXNMaXN0W1tpXV1bb3JkZXIocmVzTGlzdFtbaV1dJHB2YWx1ZSksXSwgbiA9IG5CZXN0KSRwYWRqW25CZXN0LmFjdHVhbF0KIyBjb250cmFzdCA9IGdzdWIocGF0dGVybiA9ICJsb2cyLipUaW1lXCAiLAojICAgICAgICAgICAgICAgICByZXBsYWNlbWVudCA9ICAiIiwKIyAgICAgICAgICAgICAgICAgeCA9IHJlc0xpc3RbW2ldXUBlbGVtZW50TWV0YWRhdGFbWzJdXVsyXSkKIyBjYXQoIiMjIyIsIGNvbnRyYXN0LCAiIFxuIikKIyBERVNlcTI6OnBsb3RNQShyZXNMaXN0W1tpXV0sIGFscGhhID0gbkJlc3QuYWxwaGEgKiAxLjAwMDAwMDAwMDAwMDAxLAojICAgICBtYWluID0gcGFzdGUoJ01BIHBsb3QgZm9yIHRvcCcsIG5CZXN0LmFjdHVhbCwgJ2ZlYXR1cmVzJywnLCcsY29udHJhc3QpKQojIGNhdCgnXG5cbicpCiMgfQoKYGBgCgojIyB7LX0KCiMgUC12YWx1ZXMgey50YWJzZXQgLnRhYnNldC1mYWRlfQoKIyMgRGlzdHJpYnV0aW9uIG9mIGFsbCBwLXZhbHVlcwoKYGBge3IgcHZhbHVlSGlzdG9ncmFtLCBjb2xsYXBzZT1UUlVFfQojIyBQLXZhbHVlIGhpc3RvZ3JhbSBwbG90CgpnZ3Bsb3QoYWxsUmVzdWx0cywgYWVzKHggPSBwdmFsdWUpKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhbHBoYT0uNSwgcG9zaXRpb249J2lkZW50aXR5JywgYmlucyA9IDUwKSArCiAgICBsYWJzKHRpdGxlPSdIaXN0b2dyYW0gb2YgdW5hZGp1c3RlZCBwLXZhbHVlcycpICsKICAgIHhsYWIoJ1VuYWRqdXN0ZWQgcC12YWx1ZXMnKSArCiAgICBmYWNldF93cmFwKCB+IGNvbnRyYXN0LCBuY29sID0gMikKCmBgYAoKVGhpcyBwbG90IHNob3dzIGEgaGlzdG9ncmFtIG9mIHRoZSB1bmFkanVzdGVkIHAtdmFsdWVzLiBJdCBtaWdodCBiZSBza2V3ZWQgcmlnaHQgb3IgbGVmdCwgb3IgZmxhdCBhcyBzaG93biBpbiB0aGUgW1dpa2lwZWRpYSBleGFtcGxlc10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvSGlzdG9ncmFtI0V4YW1wbGVzKS4gVGhlIHNoYXBlIGRlcGVuZHMgb24gdGhlIHBlcmNlbnQgb2YgZmVhdHVyZXMgdGhhdCBhcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkLiBGb3IgZnVydGhlciBpbmZvcm1hdGlvbiBvbiBob3cgdG8gaW50ZXJwcmV0IGEgaGlzdG9ncmFtIG9mIHAtdmFsdWVzIGNoZWNrIFtEYXZpZCBSb2JpbnNvbidzIHBvc3Qgb24gdGhpcyB0b3BpY10oaHR0cDovL3ZhcmlhbmNlZXhwbGFpbmVkLm9yZy9zdGF0aXN0aWNzL2ludGVycHJldGluZy1wdmFsdWUtaGlzdG9ncmFtLykuCgpgYGB7ciBwdmFsdWVTdW1tfQojIyBQLXZhbHVlIGRpc3RyaWJ1dGlvbiBzdW1tYXJ5CnN1bW1hcnkoYWxsUmVzdWx0cyRwdmFsdWUpCmBgYAoKVGhpcyBpcyB0aGUgbnVtZXJpY2FsIHN1bW1hcnkgb2YgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgcC12YWx1ZXMuCgpgYGB7ciBwdmFsdWVUYWJsZSwgcmVzdWx0cyA9ICdhc2lzJ30KIyMgU3BsaXQgZmVhdHVyZXMgYnkgZGlmZmVyZW50IHAtdmFsdWUgY3V0b2ZmcwpwdmFsX3RhYmxlIDwtIGxhcHBseShjKDFlLTA0LCAwLjAwMSwgMC4wMSwgMC4wMjUsIDAuMDUsIDAuMSwgMC4yLCAwLjMsIDAuNCwgMC41LAogICAgMC42LCAwLjcsIDAuOCwgMC45LCAxKSwgZnVuY3Rpb24oeCkgewogICAgZGF0YS5mcmFtZSgnQ3V0JyA9IHgsICdDb3VudCcgPSBzdW0oYWxsUmVzdWx0cyRwdmFsdWUgPD0geCwgbmEucm0gPSBUUlVFKSkKfSkKcHZhbF90YWJsZSA8LSBkby5jYWxsKHJiaW5kLCBwdmFsX3RhYmxlKQprYWJsZShwdmFsX3RhYmxlLCBmb3JtYXQgPSAnbWFya2Rvd24nLCBhbGlnbiA9IGMoJ2MnLCAnYycpKQpgYGAKClRoaXMgdGFibGUgc2hvd3MgdGhlIG51bWJlciBvZiBmZWF0dXJlcyB3aXRoIHAtdmFsdWVzIGxlc3Mgb3IgZXF1YWwgdGhhbiBzb21lIGNvbW1vbmx5IHVzZWQgY3V0b2ZmIHZhbHVlcy4gCgojIyBEaXN0cmlidXRpb24gb2YgYWRqdXN0ZWQgcC12YWx1ZXMKCmBgYHtyIHBhZGpIaXN0b2dyYW0sIGNvbGxhcHNlPVRSVUV9CiMjIEFkanVzdGVkIHAtdmFsdWVzIGhpc3RvZ3JhbSBwbG90CmdncGxvdChhbGxSZXN1bHRzLCBhZXMoeCA9IHBhZGopKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhbHBoYT0uNSwgcG9zaXRpb249J2lkZW50aXR5JywgYmlucyA9IDUwKSArCiAgICBsYWJzKHRpdGxlPXBhc3RlKCdIaXN0b2dyYW0gb2YnLCBlbGVtZW50TWV0YWRhdGEocmVzTGlzdFtbMV1dKSRkZXNjcmlwdGlvbltncmVwKCdhZGp1c3RlZCcsIGVsZW1lbnRNZXRhZGF0YShyZXNMaXN0W1sxXV0pJGRlc2NyaXB0aW9uKV0pKSArCiAgICB4bGFiKCdBZGp1c3RlZCBwLXZhbHVlcycpICsKICAgIHhsaW0oYygwLCAxLjAwMDUpKSArCiAgZmFjZXRfd3JhcCggfiBjb250cmFzdCwgbmNvbCA9IDIsIHNjYWxlcz0iZnJlZSIpCmBgYAoKVGhpcyBwbG90IHNob3dzIGEgaGlzdG9ncmFtIG9mIHRoZSBgciBlbGVtZW50TWV0YWRhdGEocmVzTGlzdFtbMV1dKSRkZXNjcmlwdGlvbltncmVwKCdhZGp1c3RlZCcsIGVsZW1lbnRNZXRhZGF0YShyZXNMaXN0W1sxXV0pJGRlc2NyaXB0aW9uKV1gLiBJdCBtaWdodCBiZSBza2V3ZWQgcmlnaHQgb3IgbGVmdCwgb3IgZmxhdCBhcyBzaG93biBpbiB0aGUgW1dpa2lwZWRpYSBleGFtcGxlc10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvSGlzdG9ncmFtI0V4YW1wbGVzKS4gCgpgYGB7ciBwYWRqU3VtbX0KIyMgQWRqdXN0ZWQgcC12YWx1ZXMgZGlzdHJpYnV0aW9uIHN1bW1hcnkKc3VtbWFyeShyZXMuZGYkcGFkaikKYGBgCgpUaGlzIGlzIHRoZSBudW1lcmljYWwgc3VtbWFyeSBvZiB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBgciBlbGVtZW50TWV0YWRhdGEocmVzTGlzdFtbMV1dKSRkZXNjcmlwdGlvbltncmVwKCdhZGp1c3RlZCcsIGVsZW1lbnRNZXRhZGF0YShyZXNMaXN0W1sxXV0pJGRlc2NyaXB0aW9uKV1gLgoKYGBge3IgcGFkalRhYmxlLCByZXN1bHRzID0gJ2FzaXMnfQojIyBTcGxpdCBmZWF0dXJlcyBieSBkaWZmZXJlbnQgYWRqdXN0ZWQgcC12YWx1ZSBjdXRvZmZzCnBhZGpfdGFibGUgPC0gbGFwcGx5KGMoMWUtMDQsIDAuMDAxLCAwLjAxLCAwLjAyNSwgMC4wNSwgMC4xLCAwLjIsIDAuMywgMC40LCAwLjUsCiAgICAwLjYsIDAuNywgMC44LCAwLjksIDEpLCBmdW5jdGlvbih4KSB7CiAgICBkYXRhLmZyYW1lKCdDdXQnID0geCwgJ0NvdW50JyA9IHN1bShyZXMuZGYkcGFkaiA8PSB4LCBuYS5ybSA9IFRSVUUpKQp9KQpwYWRqX3RhYmxlIDwtIGRvLmNhbGwocmJpbmQsIHBhZGpfdGFibGUpCmthYmxlKHBhZGpfdGFibGUsIGZvcm1hdCA9ICdtYXJrZG93bicsIGFsaWduID0gYygnYycsICdjJykpCmBgYAoKVGhpcyB0YWJsZSBzaG93cyB0aGUgbnVtYmVyIG9mIGZlYXR1cmVzIHdpdGggYHIgZWxlbWVudE1ldGFkYXRhKHJlc0xpc3RbWzFdXSkkZGVzY3JpcHRpb25bZ3JlcCgnYWRqdXN0ZWQnLCBlbGVtZW50TWV0YWRhdGEocmVzTGlzdFtbMV1dKSRkZXNjcmlwdGlvbildYCBsZXNzIG9yIGVxdWFsIHRoYW4gc29tZSBjb21tb25seSB1c2VkIGN1dG9mZiB2YWx1ZXMuIAoKIyBUYWJsZXMgb2YgdG9wIGZlYXR1cmVzCgpUaGlzIHRhYmxlIHNob3dzIHRoZSBzaWduaWZpY2FudCBERUdzIChwYXNzaW5nIGFsbCBmaWx0ZXJpbmcgY3JpdGVyaWEpIG9yZGVyZWQgYnkgdGhlaXIgYWJzb2x1dGUgZm9sZCBjaGFuZ2UuIFVzZSB0aGUgc2VhcmNoIGZ1bmN0aW9uIHRvIGZpbmQgeW91ciBmZWF0dXJlIG9mIGludGVyZXN0IG9yIHNvcnQgYnkgb25lIG9mIHRoZSBjb2x1bW5zLiBZb3UgY2FuIGxpbWl0IHRvIGEgc2luZ2xlIGNvbnRyYXN0IGlmIGRlc2lyZWQuCgpgYGB7ciAndG9wRmVhdHVyZXMnLCByZXN1bHRzID0gJ2FzaXMnLCB3YXJuaW5nID0gRkFMU0V9CnNlYXJjaFVSTCA8LSAiaHR0cDovL3d3dy5uY2JpLm5sbS5uaWguZ292L2dlbmUvP3Rlcm09IgojIyBBZGQgc2VhcmNoIHVybCBpZiBhcHByb3ByaWF0ZQpyZXMuZGYuZHQgPC0gcmVzLmRmCmlmKCFpcy5udWxsKHNlYXJjaFVSTCkpIHsKICAgIHJlcy5kZi5kdCRlbnNlbWJsX2dlbmVfaWQgPC0gcGFzdGUwKCc8YSBocmVmPSInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlYXJjaFVSTCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXMuZGYuZHQkZW5zZW1ibF9nZW5lX2lkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICciIHJlbD0ibm9vcGVuZXIgbm9yZWZlcnJlciIgdGFyZ2V0PSJfYmxhbmsiPicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzLmRmLmR0JGVuc2VtYmxfZ2VuZV9pZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPGJyLz4nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcy5kZi5kdCRleHRlcm5hbF9nZW5lX25hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJzwvYT4nKQp9CgpyZXMuZGYuZHRbLCAncGFkaiddIDwtIGZvcm1hdChyZXMuZGYuZHRbLCAncGFkaiddLCBzY2llbnRpZmljID0gVFJVRSwgZGlnaXRzPWRpZ2l0cykKcmVzLmRmLmR0WywgJ3B2YWx1ZSddIDwtIGZvcm1hdChyZXMuZGYuZHRbLCAncHZhbHVlJ10sIHNjaWVudGlmaWMgPSBUUlVFLCBkaWdpdHM9ZGlnaXRzKQpyZXMuZGYuZHQgPC0gcmVzLmRmLmR0ICU+JSBkcGx5cjo6c2VsZWN0KC1jKGRlc2NyaXB0aW9uLCBleHRlcm5hbF9nZW5lX25hbWUpKQoKRFQ6OmRhdGF0YWJsZShyZXMuZGYuZHQsCiAgICAgICAgICBvcHRpb25zID0gbGlzdChwYWdpbmdUeXBlPSdmdWxsX251bWJlcnMnLAogICAgICAgICAgICAgICAgICAgICAgICAgcGFnZUxlbmd0aD0yMCwKICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFg9JzEwMCUnLAogICAgICAgICAgICAgICAgICAgICAgICAgZG9tID0gJ0JmcnRpcCcsCiAgICAgICAgICAgICAgICAgICAgICAgICBidXR0b25zID0gYygnY29weScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnY3N2JywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdleGNlbCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAncGRmJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdwcmludCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnY29sdmlzJyksCiAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5EZWZzID0gbGlzdChsaXN0KHZpc2libGU9RkFMU0UsIHRhcmdldHM9YygyLDQsNSkpKSksCiAgICAgICAgICBlc2NhcGUgPSBGQUxTRSwKICAgICAgICAgIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsCiAgICAgICAgICByb3duYW1lcyA9IEZBTFNFLAogICAgICAgICAgZmlsdGVyID0gInRvcCIsCiAgICAgICAgICBjb2xuYW1lcyA9IGMoJ0dlbmUnID0gJ2Vuc2VtYmxfZ2VuZV9pZCcpKSAlPiUgCiAgRFQ6OmZvcm1hdFJvdW5kKHdoaWNoKCFjb2xuYW1lcyhyZXMuZGYuZHQpICVpbiUgYygncHZhbHVlJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdwYWRqJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdGZWF0dXJlJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdjb250cmFzdCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnZGVzY3JpcHRpb24nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2Vuc2VtYmxfZ2VuZV9pZCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnZXh0ZXJuYWxfZ2VuZV9uYW1lJykpLAogICAgICAgICAgICAgICAgICBkaWdpdHMpCgpgYGAKCiMgR2VuZS1sZXZlbCBwbG90cyBmb3IgdG9wIGByIG5CZXN0RmVhdHVyZXNgIGZlYXR1cmVzCgpUaGlzIHNlY3Rpb24gY29udGFpbnMgcGxvdHMgc2hvd2luZyB0aGUgbm9ybWFsaXplZCBjb3VudHMgcGVyIHNhbXBsZSBmb3IgZWFjaCBncm91cCBvZiBpbnRlcmVzdC4gT25seSB0aGUgYmVzdCBgciBuQmVzdEZlYXR1cmVzYCBmZWF0dXJlcyBhcmUgc2hvd24sIHJhbmtlZCBieSB0aGVpciBhYnNvbHV0ZSBmb2xkIGNoYW5nZSB2YWx1ZXMuIFRoZSBZIGF4aXMgaXMgb24gdGhlIGxvZzEwIHNjYWxlIGFuZCB0aGUgZmVhdHVyZSBuYW1lIGlzIHNob3duIGluIHRoZSB0aXRsZSBvZiBlYWNoIHBsb3QuCgpgYGB7ciAncGxvdENvdW50cyd9CnBsb3RDb3VudHNfZ2cgPC0gZnVuY3Rpb24oaSwgZGRzLCBpbnRncm91cCkgewogICAgcGxvdGRhdGEgPC0gcGxvdENvdW50cyhkZHMsCiAgICAgICAgICAgICAgICAgICAgICAgZ2VuZT1pLAogICAgICAgICAgICAgICAgICAgICAgIGludGdyb3VwPXBhcmFtcyRpbnRncm91cCwKICAgICAgICAgICAgICAgICAgICAgICByZXR1cm5EYXRhID0gVFJVRSkKICAgIHBsb3RfdGl0bGUgPC0gcGFzdGUoaWRfdGFibGUkZXh0ZXJuYWxfZ2VuZV9uYW1lW2lkX3RhYmxlW1tiaW9tYXJ0X2ZpbHRlcl1dID09IGldLAogICAgICAgICAgICAgICAgICAgICAgICBpZF90YWJsZSRlbnNlbWJsX2dlbmVfaWRbaWRfdGFibGVbW2Jpb21hcnRfZmlsdGVyXV0gPT0gaV0pCiAgICBpZihuY29sKHBsb3RkYXRhKSA+IDIpIHsKICAgICAgY29sb3JDb2wgPSAzCiAgICB9IGVsc2Uge2NvbG9yQ29sID0gMn0KICAgIGdncGxvdChwbG90ZGF0YSwgYWVzKHggPSBwbG90ZGF0YVssMl0sIHkgPSBwbG90ZGF0YVssMV0sIGNvbG9yID0gcGxvdGRhdGFbLGNvbG9yQ29sXSkpICsgCiAgICAgIGdlb21fcG9pbnQocG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXJkb2RnZSgpKSArCiAgICAgIHlsYWIoJ05vcm1hbGl6ZWQgY291bnQnKSArCiAgICAgIHhsYWIoJ0dyb3VwJykgKwogICAgICBnZ3RpdGxlKHBsb3RfdGl0bGUpICsKICAgICAgY29vcmRfdHJhbnMoeSA9ICJsb2cxMCIpICsKICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgKwogICAgICBsYWJzKGNvbG9yID0gY29sbmFtZXMocGxvdGRhdGEpW2NvbG9yQ29sXSkKfQoKZ2VuZXNUb1Bsb3QgPC0gc2lnbmlmaWNhbnRSZXN1bHRzICU+JSBhcnJhbmdlKC1hYnMobG9nMkZvbGRDaGFuZ2UpKQoKZm9yKGkgaW4gaGVhZCh1bmlxdWUoZ2VuZXNUb1Bsb3RbW2Jpb21hcnRfZmlsdGVyXV0pLCBuQmVzdEZlYXR1cmVzKSkgewogIHByaW50KHBsb3RDb3VudHNfZ2coaSwgZGRzID0gZGRzLCBpbnRncm91cCA9IHBhcmFtcyRpbnRncm91cCkpCn0KCmBgYAoKIyBQbG90cyBvZiBnZW5lcyBvZiBpbnRlcmVzdAoKVGhpcyBzZWN0aW9uIHNob3dzIGdlbmVzIG9mIGludGVyZXN0IHNvcnRlZCBieSB0aG9zZSB3aXRoIGhpZ2hlc3QgZm9sZC1jaGFuZ2Ugd2l0aGluIGVhY2ggY29udHJhc3QuCgpgYGB7ciAncGxvdF9nZW5lc19vZl9pbnRlcmVzdCcsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02fQoKI251bVJlc3VsdHM9MjAKbnVtUmVzdWx0cyA8LSBuQmVzdEZlYXR1cmVzCgphbGxSZXN1bHRzT3JkZXJlZF9sb2dGQ19maWx0ZXIgJT4lCiAgZ3JvdXBfYnkoY29udHJhc3QpICU+JQogIHRvcF9uKG51bVJlc3VsdHMsIHd0PWFicyhsb2cyRm9sZENoYW5nZSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoY29udHJhc3Q9YXMuZmFjdG9yKGNvbnRyYXN0KSwKICAgICAgICAgZXh0ZXJuYWxfZ2VuZV9uYW1lPXJlb3JkZXJfd2l0aGluKGV4dGVybmFsX2dlbmVfbmFtZSxsb2cyRm9sZENoYW5nZSwgY29udHJhc3QpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9bG9nMkZvbGRDaGFuZ2UsCiAgICAgICAgICAgICB5PWV4dGVybmFsX2dlbmVfbmFtZSwKICAgICAgICAgICAgIGNvbG9yPWNvbnRyYXN0LAogICAgICAgICAgICAgc2l6ZT0tbG9nKHBhZGopKSkgKwogIGdlb21fcG9pbnQoc2hvdy5sZWdlbmQgPSBUUlVFKSArCiAgZmFjZXRfd3JhcCh+Y29udHJhc3QsCiAgICAgICAgICAgICBzY2FsZXM9ImZyZWVfeSIsCiAgICAgICAgICAgICBuY29sPTQsCiAgICAgICAgICAgICBsYWJlbGxlciA9IGxhYmVsbGVyKGNvbnRyYXN0ID0gbGFiZWxfd3JhcF9nZW4oMTApKSkgKwogIHNjYWxlX3lfcmVvcmRlcmVkKCkgICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9MCwKICAgICAgICAgICAgIGxpbmV0eXBlPSJkYXNoZWQiLAogICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLAogICAgICAgICAgICAgc2l6ZT0xKSArCiAgZ2d0aXRsZShwYXN0ZTAoIlRvcCAiLAogICAgICAgICAgICAgICAgIG51bVJlc3VsdHMsCiAgICAgICAgICAgICAgICAgIiBnZW5lcyByYW5rZWQgYnkgZm9sZCBjaGFuZ2UgKGFkanVzdGVkIHAtdmFsdWUgPCIsCiAgICAgICAgICAgICAgICAgYWxwaGEsCiAgICAgICAgICAgICAgICAgIiksIGdyb3VwZWQgYnkgdHJlYXRtZW50IikpCmBgYAoKIyBWb2xjYW5vIHBsb3QKClRoaXMgc2VjdGlvbiBzaG93cyBhIHZvbGNhbm8gcGxvdCBmb3IgZWFjaCBjb250cmFzdC4gIAoKTm90ZSB0aGF0IHNjYWxlcyBhcmUgc2V0IG1hbnVhbGx5IGZvciB0aGlzIHBsb3Q6IHRoZXJlZm9yZSwgdGhlcmUgbWF5IGJlIGRhdGEgcG9pbnRzIG91dHNpZGUgdGhlIHJhbmdlIHNob3duIChzZWUgd2FybmluZ3MpLiAgCgpEYXRhIHJlcG9ydGVkIGFzIHNpZ25pZmljYW50IGFyZSBzaG93biBhcyByZWQgcG9pbnRzLgoKYGBge3IgJ3ZvbGNhbm8tcGxvdCcsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02LCB3YXJuaW5nPVRSVUUsIGNvbGxhcHNlPVRSVUV9CmdncGxvdChhbGxSZXN1bHRzLCBhZXMoeD1sb2cyRm9sZENoYW5nZSwgeT0tbG9nMTAocGFkaikpKSArCiAgZ2VvbV9wb2ludChzaXplPTAuNSwgYWxwaGE9MC40KSArCiAgZ2VvbV9wb2ludChkYXRhPXNpZ25pZmljYW50UmVzdWx0cywgc2l6ZT0xLjUsIGFscGhhPTEsIGNvbG9yPSJyZWQiKSArCiAgZmFjZXRfd3JhcCh+Y29udHJhc3QsIG5jb2w9NCkgKyAjICwgc2NhbGVzPSJmcmVlIgogIGdlb21fdmxpbmUoeGludGVyY2VwdD1jKC0xLjUsMS41KSwgY29sb3I9InJlZCIsIGFscGhhPTEuMCkrIAogIGdlb21faGxpbmUoeWludGVyY2VwdD0tbG9nMTAoMC4wNSksIGNvbG9yPSJibHVlIiwgYWxwaGE9MS4wKSArCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAibG9nMiBGb2xkIENoYW5nZSIsIGxpbWl0cyA9IGMoLTUsNSkpICsgIyAKICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZSA9ICItbG9nMTAgYWRqdXN0ZWQgcC12YWx1ZSIsIGxpbWl0cyA9IGMoMCw2KSkgIyAKYGBgCgpgYGB7ciAnYm1kZXhwcmVzc19pbnB1dCcsIGNvbGxhcHNlPVRSVUV9CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMjIyMjIyMjIFBST0RVQ0UgSU5QVVQgRk9SIEJNREV4cHJlc3MyICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFJ1biB0aGlzIGNvZGUgY29uZGl0aW9uYWwgb24gd2hldGhlciB0aGVyZSBpcyBhIGNvbHVtbiBtYXRjaGluZyB0aGUgd29yZCBkb3NlIGluIHRoZSBtZXRhZGF0YQppZihhbnkoZ3JlcGwoeD1jb2xuYW1lcyhERVNlcURlc2lnbiksIHBhdHRlcm49ImRvc2UiLCBpZ25vcmUuY2FzZSA9IFQpKSl7CiAgZG9zZUNvbCA8LSBncmVwKHg9Y29sbmFtZXMoREVTZXFEZXNpZ24pLCBwYXR0ZXJuPSJkb3NlIiwgaWdub3JlLmNhc2UgPSBUKQogICMgQ3JlYXRlIGlucHV0IGZpbGUuLi4KICBsb2dub3JtLnJlYWQuY291bnRzIDwtIGFzLmRhdGEuZnJhbWUoY291bnRzKGRkcywgbm9ybWFsaXplZD1UUlVFKSkKICAKICAjIHJsZCBub3JtYWxpemVkLCBzaXplIGZhY3RvciBub3JtYWxpemVkLCByb3VuZGVkIHRvIDQgc2lnbmlmaWNhbnQgZmlndXJlcwogIGJtZGV4cHJlc3MgPC0gZmFzdGJtZCA8LSBhcy5kYXRhLmZyYW1lKGxvZ25vcm0ucmVhZC5jb3VudHMpCiAgYm1kZXhwcmVzcyA8LSBjYmluZChTYW1wbGVJRD1yb3cubmFtZXMoYm1kZXhwcmVzcyksIGJtZGV4cHJlc3MsIHN0cmluZ3NBc0ZhY3RvcnM9RikKICBibWRleHByZXNzIDwtIHJiaW5kKCBEb3NlPWMoIiNDTEFTUzpET1NFIiwgYXMubnVtZXJpYyhERVNlcURlc2lnblssZG9zZUNvbF0pKSwgYm1kZXhwcmVzcywgc3RyaW5nc0FzRmFjdG9ycz1GKQogIAogICMgQ3JlYXRlIGlucHV0IGZvciBmYXN0Ym1kIGFsc28KICBmYXN0Ym1kIDwtIGNiaW5kKFNhbXBsZUlEPXJvdy5uYW1lcyhmYXN0Ym1kKSwgZmFzdGJtZCwgc3RyaW5nc0FzRmFjdG9ycz1GKQogICMgT3B0aW9uYWxseS4uLiB5b3UgY2FuIGFkZCBhIHNlY29uZCB2YXJpYWJsZSBuYW1lIChlLmcuLCBjaGVtaWNhbCkgZm9yIGZhc3RibWQgb25seQogICMgZmFzdGJtZCA8LSByYmluZCggQ2hlbWljYWw9YygiI0NMQVNTOkNIRU1JQ0FMIixhcy5jaGFyYWN0ZXIoREVTZXFEZXNpZ25bREVTZXFEZXNpZ24kb3JpZ2luYWxfbmFtZXMgJWluJSBjb2xuYW1lcyhmYXN0Ym1kKSxdJGNoZW1pY2FsKSksCiAgIyAgICAgICAgICAgICAgICAgIGZhc3RibWQsCiAgIyAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnM9RikKICBmYXN0Ym1kIDwtIHJiaW5kKCBEb3NlPWMoIiNDTEFTUzpET1NFIixmb3JtYXQoREVTZXFEZXNpZ25bREVTZXFEZXNpZ24kb3JpZ2luYWxfbmFtZXMgJWluJSBjb2xuYW1lcyhmYXN0Ym1kKSxdJGRvc2UsIHNjaWVudGlmaWM9RikpLAogICAgICAgICAgICAgICAgICAgIGZhc3RibWQsCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycz1GKQogIAogIGlmKCFpcy5uYShwYXJhbXMkZ3JvdXBfZmFjZXQpKSB7CiAgICB3cml0ZS50YWJsZShibWRleHByZXNzLAogICAgICAgICAgICAgICAgZmlsZT1maWxlLnBhdGgocGF0aHMkREVHX291dHB1dCwgcGFzdGUwKCJibWRleHByZXNzX2lucHV0XyIsIHBhcmFtcyRncm91cF9maWx0ZXIsICIudHh0IikpLAogICAgICAgICAgICAgICAgcXVvdGUgPSBGLAogICAgICAgICAgICAgICAgc2VwID0gIlx0IiwKICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICBjb2wubmFtZXMgPSBUKQogICAgd3JpdGUudGFibGUoZmFzdGJtZCwKICAgICAgICAgICAgICAgIGZpbGU9ZmlsZS5wYXRoKHBhdGhzJERFR19vdXRwdXQsIHBhc3RlMCgiZmFzdGJtZF9pbnB1dF8iLCBwYXJhbXMkZ3JvdXBfZmlsdGVyLCAiLnR4dCIpKSwKICAgICAgICAgICAgICAgIHF1b3RlID0gRiwKICAgICAgICAgICAgICAgIHNlcCA9ICJcdCIsCiAgICAgICAgICAgICAgICByb3cubmFtZXMgPSBGLAogICAgICAgICAgICAgICAgY29sLm5hbWVzID0gVCkKICB9IGVsc2UgewogICAgd3JpdGUudGFibGUoYm1kZXhwcmVzcywKICAgICAgICAgICAgICAgIGZpbGU9ZmlsZS5wYXRoKHBhdGhzJERFR19vdXRwdXQsICJibWRleHByZXNzX2lucHV0LnR4dCIpLAogICAgICAgICAgICAgICAgcXVvdGUgPSBGLAogICAgICAgICAgICAgICAgc2VwID0gIlx0IiwKICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICBjb2wubmFtZXMgPSBUKQogICAgd3JpdGUudGFibGUoZmFzdGJtZCwKICAgICAgICAgICAgICAgIGZpbGU9ZmlsZS5wYXRoKHBhdGhzJERFR19vdXRwdXQsICJmYXN0Ym1kX2lucHV0LnR4dCIpLAogICAgICAgICAgICAgICAgcXVvdGUgPSBGLAogICAgICAgICAgICAgICAgc2VwID0gIlx0IiwKICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IEYsCiAgICAgICAgICAgICAgICBjb2wubmFtZXMgPSBUKQogIH0KfQpgYGAKCiMgR08gRW5yaWNobWVudCBBbmFseXNpcyB7LnRhYnNldCAudGFic2V0LWZhZGV9CgpUaGlzIHNlY3Rpb24gcGVyZm9ybXMgR08gZW5yaWNobWVudCBvbiAqKmFsbCBERUdzIHBhc3NpbmcgZmlsdGVycyoqLiBUaGUgYmFja2dyb3VuZCBzZXQgb2YgZ2VuZXMgaXMgdGhvc2UgdGhhdCB3ZXJlIGlkZW50aWZpZWQgaW4gdGhpcyBzZXF1ZW5jaW5nIGV4cGVyaW1lbnQuIFRoZSBbY2x1c3RlclByb2ZpbGVyXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvY2x1c3RlclByb2ZpbGVyLmh0bWwpIHBhY2thZ2UgaXMgdXNlZCBmb3IgdGhpcyBhbmFseXNpcy4KCmBgYHtyIGNsdXN0ZXItcHJvZmlsZXIsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGNvbGxhcHNlPVRSVUV9CiMjIFJlbWVtYmVyIHRoYXQgZGRzIGhhZCBFTlNFTUJMIGlkcyBmb3IgdGhlIGdlbmVzCiMgZW5zZW1ibE5hbWVzIDwtIGdzdWIoIlxcLi4qIiwgIiIsIHJvd25hbWVzKGRkcykpCgplbnNlbWJsREVHcyA8LSBzaWduaWZpY2FudFJlc3VsdHNbW2Jpb21hcnRfZmlsdGVyXV0KaGVhZChlbnNlbWJsREVHcykKREVHcyA8LSBkcGx5cjo6bGVmdF9qb2luKGRhdGEuZnJhbWUoZ2VuZXM9ZW5zZW1ibERFR3MpLCBpZF90YWJsZV9lbnRyZXosIGJ5PWMoImdlbmVzIiA9IGJpb21hcnRfZmlsdGVyKSkKaGVhZChERUdzKQplbnRyZXpERUdzIDwtIGFzLmNoYXJhY3RlcihERUdzJGVudHJlemdlbmVfaWQpCmVudHJlekRFR3MgPC0gZW50cmV6REVHc1shaXMubmEoZW50cmV6REVHcyldCgojIyMjIERFRklORSBJTlBVVFMKbXlERUdzIDwtIGVudHJlekRFR3MKIyBiYWNrZ3JvdW5kIDwtIHJvdy5uYW1lcyhhc3NheShkZHMpKQpiYWNrZ3JvdW5kIDwtIGRwbHlyOjpsZWZ0X2pvaW4oZGF0YS5mcmFtZShnZW5lcz1yb3cubmFtZXMoYXNzYXkoZGRzKSkpLCBpZF90YWJsZV9lbnRyZXosIGJ5PWMoImdlbmVzIj1iaW9tYXJ0X2ZpbHRlcikpICU+JQogIGRwbHlyOjpmaWx0ZXIoIWlzLm5hKGVudHJlemdlbmVfaWQpKSAlPiUKICBkcGx5cjo6cHVsbChlbnRyZXpnZW5lX2lkKQpiYWNrZ3JvdW5kIDwtIGFzLmNoYXJhY3RlcihiYWNrZ3JvdW5kKQoKZW50cmV6REVHcyA8LSBlbnRyZXpERUdzWyFpcy5uYShlbnRyZXpERUdzKV0KCiMjIyMjIFRPIERPOiBBZGQgbXVsdGlwbGUgcGFuZWxzIGZvciBlYWNoIGNvbnRyYXN0LgojIyMjIyBVc2UgImFsbCIgYXJndW1lbnQgdG8gb25seSBydW4gZWFjaCBvbmNlLi4/CgojIyBOb3QgYWxsIGdlbmVzIGhhdmUgYSBwLXZhbHVlCnRhYmxlKCFpcy5uYShyZXNMaXN0W1sxXV0kcGFkaikpCgpLZXlUeXBlIDwtICJFTlRSRVpJRCIgIyBFTlNFTUJMPyBBQ0NOVU0/IEdJRD8gRU5UUkVaSUQ/CiMgaGVhZChrZXlzKG9yZ2RiLCBrZXl0eXBlPSJFTlRSRVpJRCIpKQoKIyBSdW4gYWxsIGF0IG9uY2UuLi4KIyBlbnJpY2hfZ29fYWxsIDwtIGVucmljaEdPKGdlbmUgPSBteURFR3MsCiMgICAgICAgICAgICAgICAgICAgICAgICAgIHVuaXZlcnNlID0gYmFja2dyb3VuZCwgIyBBbGwgZ2VuZXMgaW4gZGF0YXNldAojICAgICAgICAgICAgICAgICAgICAgICAgICBPcmdEYiA9IG9yZ2RiLAojICAgICAgICAgICAgICAgICAgICAgICAgICBrZXlUeXBlID0gS2V5VHlwZSwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgcmVhZGFibGU9VCwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgb250ID0gImFsbCIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgIHBBZGp1c3RNZXRob2QgPSAiQkgiLAojICAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgPSAwLjAxLAojICAgICAgICAgICAgICAgICAgICAgICAgICBxdmFsdWVDdXRvZmYgPSAwLjA1KQoKIyBjbHVzdGVyUHJvZmlsZXI6OmRvdHBsb3QoZW5yaWNoX2dvX2FsbCwgZm9udC5zaXplPTksIHNob3dDYXRlZ29yeT0xMCwgc3BsaXQ9Ik9OVE9MT0dZIikgICsKIyAgIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCkpICsKIyAgIHNjYWxlX3lfZGlzY3JldGUobGFiZWxzID0gZnVuY3Rpb24oeCkgc3RyaW5ncjo6c3RyX3dyYXAoeCwgd2lkdGggPSA1MCkpICsKIyAgIGZhY2V0X3dyYXAofk9OVE9MT0dZKQoKIyMgQ2FuIGFsc28gZG8gaW5kaXZpZHVhbGx5Li4uCiMjIFBlcmZvcm0gZW5yaWNobWVudCBhbmFseXNpcyBmb3IgQmlvbG9naWNhbCBQcm9jZXNzIChCUCkKZW5yaWNoX2dvX2JwIDwtIGVucmljaEdPKGdlbmUgPSBteURFR3MsCiAgICAgICAgICAgICAgICAgICAgICAgICB1bml2ZXJzZSA9IGJhY2tncm91bmQsICMgQWxsIGdlbmVzIGluIGRhdGFzZXQKICAgICAgICAgICAgICAgICAgICAgICAgIE9yZ0RiID0gb3JnZGIsCiAgICAgICAgICAgICAgICAgICAgICAgICBrZXlUeXBlID0gS2V5VHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgIHJlYWRhYmxlPVQsCiAgICAgICAgICAgICAgICAgICAgICAgICBvbnQgPSAiQlAiLAogICAgICAgICAgICAgICAgICAgICAgICAgcEFkanVzdE1ldGhvZCA9ICJCSCIsCiAgICAgICAgICAgICAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgPSAwLjAxLAogICAgICAgICAgICAgICAgICAgICAgICAgcXZhbHVlQ3V0b2ZmID0gMC4wNSkKCmVucmljaF9nb19tZiA8LSBlbnJpY2hHTyhnZW5lID0gbXlERUdzLAogICAgICAgICAgICAgICAgICAgICAgICAgdW5pdmVyc2UgPSBiYWNrZ3JvdW5kLCAjIEFsbCBnZW5lcyBpbiBkYXRhc2V0CiAgICAgICAgICAgICAgICAgICAgICAgICBPcmdEYiA9IG9yZ2RiLAogICAgICAgICAgICAgICAgICAgICAgICAga2V5VHlwZSA9IEtleVR5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICByZWFkYWJsZT1ULAogICAgICAgICAgICAgICAgICAgICAgICAgb250ID0gIk1GIiwKICAgICAgICAgICAgICAgICAgICAgICAgIHBBZGp1c3RNZXRob2QgPSAiQkgiLAogICAgICAgICAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmID0gMC4wMSwKICAgICAgICAgICAgICAgICAgICAgICAgIHF2YWx1ZUN1dG9mZiA9IDAuMDUpCgplbnJpY2hfZ29fY2MgPC0gZW5yaWNoR08oZ2VuZSA9IG15REVHcywKICAgICAgICAgICAgICAgICAgICAgICAgIHVuaXZlcnNlID0gYmFja2dyb3VuZCwgIyBBbGwgZ2VuZXMgaW4gZGF0YXNldAogICAgICAgICAgICAgICAgICAgICAgICAgT3JnRGIgPSBvcmdkYiwKICAgICAgICAgICAgICAgICAgICAgICAgIGtleVR5cGUgPSBLZXlUeXBlLAogICAgICAgICAgICAgICAgICAgICAgICAgb250ID0gIkNDIiwKICAgICAgICAgICAgICAgICAgICAgICAgIHBBZGp1c3RNZXRob2QgPSAiQkgiLAogICAgICAgICAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmID0gMC4wMSwKICAgICAgICAgICAgICAgICAgICAgICAgIHF2YWx1ZUN1dG9mZiA9IDAuMDUpCgpgYGAKCiMjIEJpb2xvZ2ljYWwgcHJvY2Vzc2VzCgpgYGB7ciBwbG90X0dPX2FuYWx5c2lzX2JwLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9MTIsIGNvbGxhcHNlPVRSVUV9CmlmIChucm93KGVucmljaF9nb19icEByZXN1bHQgJT4lIGZpbHRlcihwLmFkanVzdCA8IDAuMDEgJiBxdmFsdWUgPCAwLjA1KSkgPiAwKSB7CiAgcGxvdDEgPC0gY2x1c3RlclByb2ZpbGVyOjpkb3RwbG90KGVucmljaF9nb19icCwgZm9udC5zaXplPTksIHNob3dDYXRlZ29yeT0yMCkgICsKICAgIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCkpICsKICAgIHNjYWxlX3lfZGlzY3JldGUobGFiZWxzID0gZnVuY3Rpb24oeCkgc3RyaW5ncjo6c3RyX3dyYXAoeCwgd2lkdGggPSAzMCkpCiAgCiAgREVHc19mdWxsIDwtIGRwbHlyOjpsZWZ0X2pvaW4oc2lnbmlmaWNhbnRSZXN1bHRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkX3RhYmxlX2VudHJleiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieT1iaW9tYXJ0X2ZpbHRlcikgJT4lCiAgICBkaXN0aW5jdCgpCiAgZm9sZENoYW5nZXMgPC0gREVHc19mdWxsICU+JSBkcGx5cjo6cHVsbChsb2cyRm9sZENoYW5nZSkKICBuYW1lcyhmb2xkQ2hhbmdlcykgPC0gREVHc19mdWxsICU+JSBkcGx5cjo6cHVsbChlbnRyZXpnZW5lX2lkKQogIGZvbGRDaGFuZ2VzIDwtIGZvbGRDaGFuZ2VzICU+JSBzb3J0KCkgJT4lIHJldigpCiAgZm9sZENoYW5nZXMgPC0gZm9sZENoYW5nZXNbIWlzLm5hKG5hbWVzKGZvbGRDaGFuZ2VzKSldCiAgCiAgc2ltcGxpZmllZF9icCA8LSBzaW1wbGlmeShlbnJpY2hfZ29fYnAsIGN1dG9mZiA9IDAuNywgYnkgPSAicC5hZGp1c3QiLCBzZWxlY3RfZnVuID0gbWluKQogICMgTWF4IHNpemUgb2YgZ2VuZSBzZXQuLi4gdXNlZnVsIGZvciB2aXN1YWxpemluZyBuZXR3b3JrcwogIHNpbXBsaWZpZWRfYnBfZm9yX25ldHdvcmsgPC0gc2ltcGxpZmllZF9icCAlPiUgRE9TRTo6Z3NmaWx0ZXIoYnk9IkdTU2l6ZSIsIG1heD00MDApCiAgIyBJIGJlbGlldmUgdGhlcmUgaXMgYSBidWcgaW4gdGhlIGN1cnJlbnQgdmVyc2lvbiBvZiBET1NFIG9yIGNsdXN0ZXJQcm9maWxlciB0aGF0IHByZXZlbnRzIHRoaXMgZnJvbSB3b3JraW5nCiAgIyBUaHJvd3MgdGhpcyBlcnJvciBvbiBwbG90dGluZzogRXJyb3IgaW4gZ3JhcGguZGF0YS5mcmFtZSh4LCBkaXJlY3RlZCA9IEZBTFNFKSA6IHRoZSBkYXRhIGZyYW1lIHNob3VsZCBjb250YWluIGF0IGxlYXN0IHR3byBjb2x1bW5zCiAgc2ltcGxpZmllZF9icF9maWx0IDwtIHNpbXBsaWZpZWRfYnAgJT4lCiAgICAgIGZpbHRlcihwLmFkanVzdCA8IC4wNSwgcXZhbHVlIDwgMC4wNSkgJT4lCiAgICAgIG11dGF0ZShHZW5lUmF0aW8gPSBET1NFOjpwYXJzZV9yYXRpbyhHZW5lUmF0aW8pKSAlPiUKICAgICAgYXJyYW5nZShkZXNjKEdlbmVSYXRpbykpCiAgCiAgc2hvdzMgPC0gc2ltcGxpZmllZF9icF9maWx0JERlc2NyaXB0aW9uWzE6M10gIyBUb3AgMwogIHNob3czIDwtIHNob3czWyFpcy5uYShzaG93MyldCiAgc2hvdzUgPC0gc2ltcGxpZmllZF9icF9maWx0JERlc2NyaXB0aW9uWzE6NV0gIyBUb3AgNQogIHNob3c1IDwtIHNob3c1WyFpcy5uYShzaG93NSldCiAgc2hvdzEwIDwtIHNpbXBsaWZpZWRfYnBfZmlsdCREZXNjcmlwdGlvblsxOjEwXSAjIFRvcCAxMAogIHNob3cxMCA8LSBzaG93MTBbIWlzLm5hKHNob3cxMCldCiAgCiAgcGxvdDIgPC0gY25ldHBsb3Qoc2ltcGxpZmllZF9icCwKICAgICAgICAgICBmb2xkQ2hhbmdlPWZvbGRDaGFuZ2VzLAogICAgICAgICAgIHNob3dDYXRlZ29yeSA9IHNob3cxMCwKICAgICAgICAgICBjZXhfbGFiZWxfZ2VuZT0wLjMpCiAgCiAgcGxvdDMgPC0gdXBzZXRwbG90KGVucmljaF9nb19icCkKICBwbG90NCA8LSBoZWF0cGxvdChzaW1wbGlmaWVkX2JwLCBmb2xkQ2hhbmdlPWZvbGRDaGFuZ2VzLCBzaG93Q2F0ZWdvcnkgPSBzaG93MTApICsKICAgIHNjYWxlX3lfZGlzY3JldGUobGFiZWxzID0gZnVuY3Rpb24oeCkgc3RyaW5ncjo6c3RyX3RydW5jKHgsIHdpZHRoID0gNTApKQogIAogIHByaW50KHBsb3QxKQogIHByaW50KHBsb3QyKQogIHByaW50KHBsb3QzKQogIHByaW50KHBsb3Q0KQp9IGVsc2UgeyBwcmludCgiTm8gc2lnbmlmaWNhbnRseSBlbnJpY2hlZCB0ZXJtcyB1c2luZyBjcml0ZXJpYSBzZWxlY3RlZCIpIH0KYGBgCgojIyBNb2xlY3VsYXIgRnVuY3Rpb25zCgpgYGB7ciBwbG90X0dPX2FuYWx5c2lzX21mLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9MTIsIGNvbGxhcHNlPVRSVUV9CmlmIChucm93KGVucmljaF9nb19tZkByZXN1bHQgJT4lIGZpbHRlcihwLmFkanVzdCA8IDAuMDEgJiBxdmFsdWUgPCAwLjA1KSkgPiAwKSB7CiAgcGxvdDEgPC0gY2x1c3RlclByb2ZpbGVyOjpkb3RwbG90KGVucmljaF9nb19tZiwgZm9udC5zaXplPTkpICArCiAgICAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwKSkgKwogICAgc2NhbGVfeV9kaXNjcmV0ZShsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJpbmdyOjpzdHJfd3JhcCh4LCB3aWR0aCA9IDMwKSkKICAKICBzaW1wbGlmaWVkX21mIDwtIHNpbXBsaWZ5KGVucmljaF9nb19tZiwgY3V0b2ZmID0gMC43LCBieSA9ICJwLmFkanVzdCIsIHNlbGVjdF9mdW4gPSBtaW4pCiAgIyBNYXggc2l6ZSBvZiBnZW5lIHNldC4uLiB1c2VmdWwgZm9yIHZpc3VhbGl6aW5nIG5ldHdvcmtzCiAgc2ltcGxpZmllZF9tZl9mb3JfbmV0d29yayA8LSBzaW1wbGlmaWVkX21mICU+JSBET1NFOjpnc2ZpbHRlcihieT0iR1NTaXplIiwgbWF4PTQwMCkKICAjIEkgYmVsaWV2ZSB0aGVyZSBpcyBhIGJ1ZyBpbiB0aGUgY3VycmVudCB2ZXJzaW9uIG9mIERPU0Ugb3IgY2x1c3RlclByb2ZpbGVyIHRoYXQgcHJldmVudHMgdGhpcyBmcm9tIHdvcmtpbmcKICAjIFRocm93cyB0aGlzIGVycm9yIG9uIHBsb3R0aW5nOiBFcnJvciBpbiBncmFwaC5kYXRhLmZyYW1lKHgsIGRpcmVjdGVkID0gRkFMU0UpIDogdGhlIGRhdGEgZnJhbWUgc2hvdWxkIGNvbnRhaW4gYXQgbGVhc3QgdHdvIGNvbHVtbnMKICBzaW1wbGlmaWVkX21mX2ZpbHQgPC0gc2ltcGxpZmllZF9tZiAlPiUKICAgICAgZmlsdGVyKHAuYWRqdXN0IDwgLjA1LCBxdmFsdWUgPCAwLjA1KSAlPiUKICAgICAgbXV0YXRlKEdlbmVSYXRpbyA9IERPU0U6OnBhcnNlX3JhdGlvKEdlbmVSYXRpbykpICU+JQogICAgICBhcnJhbmdlKGRlc2MoR2VuZVJhdGlvKSkKICAKICBzaG93MyA8LSBzaW1wbGlmaWVkX21mX2ZpbHQkRGVzY3JpcHRpb25bMTozXSAjIFRvcCAzCiAgc2hvdzUgPC0gc2ltcGxpZmllZF9tZl9maWx0JERlc2NyaXB0aW9uWzE6NV0gIyBUb3AgNQogIHNob3cxMCA8LSBzaW1wbGlmaWVkX21mX2ZpbHQkRGVzY3JpcHRpb25bMToxMF0gIyBUb3AgMTAKICAKICBwbG90MiA8LSBjbmV0cGxvdChzaW1wbGlmaWVkX21mLAogICAgICAgICAgIGZvbGRDaGFuZ2U9Zm9sZENoYW5nZXMsCiAgICAgICAgICAgc2hvd0NhdGVnb3J5ID0gc2hvdzEwLAogICAgICAgICAgIGNleF9sYWJlbF9nZW5lPTAuMykKICAKICBlbnJpY2hfZ29fbWZfdXBzZXQgPC0gZW5yaWNoX2dvX21mCiAgZW5yaWNoX2dvX21mX3Vwc2V0QHJlc3VsdCREZXNjcmlwdGlvbiA8LSBzdHJfdHJ1bmMoZW5yaWNoX2dvX21mX3Vwc2V0QHJlc3VsdCREZXNjcmlwdGlvbiwgd2lkdGggPSA1MCkKICBwbG90MyA8LSB1cHNldHBsb3QoZW5yaWNoX2dvX21mX3Vwc2V0KQogIHBsb3Q0IDwtIGVucmljaHBsb3Q6OmhlYXRwbG90KHNpbXBsaWZpZWRfbWYsIGZvbGRDaGFuZ2U9Zm9sZENoYW5nZXMsIHNob3dDYXRlZ29yeSA9IHNob3cxMCkgKwogICAgc2NhbGVfeV9kaXNjcmV0ZShsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJpbmdyOjpzdHJfdHJ1bmMoeCwgd2lkdGggPSA1MCkpCiAgCiAgcHJpbnQocGxvdDEpCiAgcHJpbnQocGxvdDIpCiAgcHJpbnQocGxvdDMpCiAgcHJpbnQocGxvdDQpCn0gZWxzZSB7IHByaW50KCJObyBzaWduaWZpY2FudGx5IGVucmljaGVkIHRlcm1zIHVzaW5nIGNyaXRlcmlhIHNlbGVjdGVkIikgfQpgYGAKCiMjIENlbGx1bGFyIENvbXBvbmVudAoKYGBge3IgcGxvdF9HT19hbmFseXNpc19jYywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoPTEyLCBjb2xsYXBzZT1UUlVFfQppZiAobnJvdyhlbnJpY2hfZ29fY2NAcmVzdWx0ICU+JSBmaWx0ZXIocC5hZGp1c3QgPCAwLjAxICYgcXZhbHVlIDwgMC4wNSkpID4gMCkgewogIHBsb3QxIDwtIGNsdXN0ZXJQcm9maWxlcjo6ZG90cGxvdChlbnJpY2hfZ29fY2MsIGZvbnQuc2l6ZT05KSAgKwogICAgIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCkpICsKICAgIHNjYWxlX3lfZGlzY3JldGUobGFiZWxzID0gZnVuY3Rpb24oeCkgc3RyaW5ncjo6c3RyX3dyYXAoeCwgd2lkdGggPSAzMCkpCiAgCiAgc2ltcGxpZmllZF9jYyA8LSBzaW1wbGlmeShlbnJpY2hfZ29fY2MsIGN1dG9mZiA9IDAuNywgYnkgPSAicC5hZGp1c3QiLCBzZWxlY3RfZnVuID0gbWluKQogICMgTWF4IHNpemUgb2YgZ2VuZSBzZXQuLi4gdXNlZnVsIGZvciB2aXN1YWxpemluZyBuZXR3b3JrcwogIHNpbXBsaWZpZWRfY2NfZm9yX25ldHdvcmsgPC0gc2ltcGxpZmllZF9jYyAlPiUgRE9TRTo6Z3NmaWx0ZXIoYnk9IkdTU2l6ZSIsIG1heD00MDApCiAgIyBJIGJlbGlldmUgdGhlcmUgaXMgYSBidWcgaW4gdGhlIGN1cnJlbnQgdmVyc2lvbiBvZiBET1NFIG9yIGNsdXN0ZXJQcm9maWxlciB0aGF0IHByZXZlbnRzIHRoaXMgZnJvbSB3b3JraW5nCiAgIyBUaHJvd3MgdGhpcyBlcnJvciBvbiBwbG90dGluZzogRXJyb3IgaW4gZ3JhcGguZGF0YS5mcmFtZSh4LCBkaXJlY3RlZCA9IEZBTFNFKSA6IHRoZSBkYXRhIGZyYW1lIHNob3VsZCBjb250YWluIGF0IGxlYXN0IHR3byBjb2x1bW5zCiAgc2ltcGxpZmllZF9jY19maWx0IDwtIHNpbXBsaWZpZWRfY2MgJT4lCiAgICAgIGZpbHRlcihwLmFkanVzdCA8IC4wNSwgcXZhbHVlIDwgMC4wNSkgJT4lCiAgICAgIG11dGF0ZShHZW5lUmF0aW8gPSBET1NFOjpwYXJzZV9yYXRpbyhHZW5lUmF0aW8pKSAlPiUKICAgICAgYXJyYW5nZShkZXNjKEdlbmVSYXRpbykpCiAgCiAgc2hvdzMgPC0gc2ltcGxpZmllZF9jY19maWx0JERlc2NyaXB0aW9uWzE6M10gIyBUb3AgMwogIHNob3c1IDwtIHNpbXBsaWZpZWRfY2NfZmlsdCREZXNjcmlwdGlvblsxOjVdICMgVG9wIDUKICBzaG93MTAgPC0gc2ltcGxpZmllZF9jY19maWx0JERlc2NyaXB0aW9uWzE6MTBdICMgVG9wIDEwCiAgCiAgcGxvdDIgPC0gY25ldHBsb3Qoc2ltcGxpZmllZF9jYywKICAgICAgICAgICBmb2xkQ2hhbmdlPWZvbGRDaGFuZ2VzLAogICAgICAgICAgIHNob3dDYXRlZ29yeSA9IHNob3cxMCwKICAgICAgICAgICBjZXhfbGFiZWxfZ2VuZT0wLjMpCiAgCiAgZW5yaWNoX2dvX2NjX3Vwc2V0IDwtIGVucmljaF9nb19jYwogIGVucmljaF9nb19jY191cHNldEByZXN1bHQkRGVzY3JpcHRpb24gPC0gc3RyX3RydW5jKGVucmljaF9nb19jY191cHNldEByZXN1bHQkRGVzY3JpcHRpb24sIHdpZHRoID0gNTApCiAgcGxvdDMgPC0gdXBzZXRwbG90KGVucmljaF9nb19jY191cHNldCkKICBwbG90NCA8LSBlbnJpY2hwbG90OjpoZWF0cGxvdChzaW1wbGlmaWVkX2NjLCBmb2xkQ2hhbmdlPWZvbGRDaGFuZ2VzLCBzaG93Q2F0ZWdvcnkgPSBzaG93MTApICsKICAgIHNjYWxlX3lfZGlzY3JldGUobGFiZWxzID0gZnVuY3Rpb24oeCkgc3RyaW5ncjo6c3RyX3RydW5jKHgsIHdpZHRoID0gNTApKQogIAogIHByaW50KHBsb3QxKQogIHByaW50KHBsb3QyKQogIHByaW50KHBsb3QzKQogIHByaW50KHBsb3Q0KQp9IGVsc2UgeyBwcmludCgiTm8gc2lnbmlmaWNhbnRseSBlbnJpY2hlZCB0ZXJtcyB1c2luZyBjcml0ZXJpYSBzZWxlY3RlZCIpIH0KYGBgCgpgYGB7ciBydW5fcGF0aHdheV9hbmFseXNpc19jaGlsZF9kb2N1bWVudCwgY2hpbGQ9cGF0aHdheV9hbmFseXNpcywgZXZhbCA9IHBhcmFtcyRydW5fcGF0aHdheV9hbmFseXNpc30KYGBgCgojIE1ldGhvZHMgU3VtbWFyeQoKUGxlYXNlIHVzZSB0aGlzIGFzIGEgc3RhcnRpbmcgcG9pbnQgZm9yIHRoZSBiaW9pbmZvcm1hdGljcy9zdGF0aXN0aWNzIHNlY3Rpb24gb2YgYW55IHB1YmxpY2F0aW9ucyBiYXNlZCBvbiB0aGUgZGF0YSBhbmFseXplZCBpbiB0aGlzIHJlcG9ydC4gQWxsIG9mIHRoZSBpbmZvcm1hdGlvbiBwcm92aWRlZCBoZXJlIGlzIGNvbnRhaW5lZCBlbHNld2hlcmUgaW4gdGhlIHJlcG9ydCwgYnV0IGl0IGlzIGFnZ3JlZ2F0ZWQgaGVyZSBmb3IgeW91ciBjb252ZW5pZW5jZS4gIAoKVGhlcmUgd2VyZSBgciBzYW1wbGVzX2JlZm9yZWAgc2FtcGxlcyBmb3Igd2hpY2ggZGF0YSB3YXMgY29sbGVjdGVkLiBBIGNvdW50IG1hdHJpeCBjb250YWluaW5nIHRoZSAkYHIgaW5pdGlhbFNhbXBsZURhdGFDb3VudGAkIHNhbXBsZXMgc2VxdWVuY2VkIGluIHRoaXMgZXhwZXJpbWVudCB3YXMgaW1wb3J0ZWQgaW50byBSIGZvciBzdGF0aXN0aWNhbCBhbmFseXNpcy4gQWZ0ZXIgcmVtb3Zpbmcgc2FtcGxlcyB3aXRoIGxlc3MgdGhhbiAkYHIgdGhyZXNob2xkYCQgcmVhZHMsIGByIGZpbHRlcmVkU2FtcGxlRGF0YUNvdW50YCBzYW1wbGVzIHdlcmUgbGVmdC4gRm9sbG93aW5nIHRoZSBleGNsdXNpb24gb2Ygb3RoZXIgc2FtcGxlcyAqKHdoaWNoIG9uZXM/IFdoeT8gUGxlYXNlIGV4cGxhaW4gZm9yIHlvdXIgZXhwZXJpbWVudCwgaWYgYXBwbGljYWJsZS4pKiwgdGhlcmUgd2VyZSBgciBzYW1wbGVzX2FmdGVyYCBzYW1wbGVzIHJlbWFpbmluZy4gRm9sbG93aW5nIHRoZSByZWNvbW1lbmRhdGlvbnMgc2V0IG91dCBieSB0aGUgT21pY3MgRGF0YSBBbmFseXNpcyBGcmFtZXdvcmtzIGZvciBSZWd1bGF0b3J5IGFwcGxpY2F0aW9uIChSLU9EQUYpIGd1aWRlbGluZXMsIGdlbmVzIHdlcmUgZmlsdGVyZWQgdG8gaW5jbHVkZSBvbmx5IHRob3NlIHdoZXJlIDc1JSBvZiBhdCBsZWFzdCBvbmUgZXhwZXJpbWVudGFsIGdyb3VwIHdlcmUgYWJvdmUgYHIgTWluQ291bnRgIENQTSwgYW5kIHNwdXJpb3VzIHNwaWtlcyB3ZXJlIHJlbW92ZWQgaW4gd2hpY2ggKG1heCAtIG1lZGlhbikgb2YgY291bnRzIHdlcmUgbGVzcyB0aGFuIChzdW0gb2YgY291bnRzKS8obnVtYmVyIG9mIHJlcGxpY2F0ZXMgKyAxKS4gVGhlIHNhbXBsZXMgZXhjbHVkZWQgZnJvbSBhbmFseXNpcyBhcmUgc2hvd24gaW4gdGhlIHRhYmxlIGJlbG93LiBXZSB1c2VkIERFU2VxMiBgciBwYWNrYWdlVmVyc2lvbigiREVTZXEyIilgIHRvIHRlc3QgZm9yIGRpZmZlcmVudGlhbGx5IGFidW5kYW50IGdlbmVzIHdpdGhpbiB0aGUgYHIgUGxhdGZvcm1gIGRhdGEuIFRoZSBsb2cyRm9sZENoYW5nZSBzaHJpbmthZ2UgcHJvY2VkdXJlIHVzZWQgd2FzIGByIHJlc0xpc3RbWzFdXUBwcmlvckluZm8kdHlwZWAuIEFuIGFscGhhIG9mIGByIHJlc0xpc3RbWzFdXUBtZXRhZGF0YSRhbHBoYWAgd2FzIHVzZWQgdG8gZXh0cmFjdCByYXcgcmVzdWx0cywgd2hpY2ggYXJlIHJlcG9ydGVkIGFzIHRoZSBgciBnc3ViKG1jb2xzKHg9cmVzTGlzdFtbMV1dKSRkZXNjcmlwdGlvbltbNF1dLHBhdHRlcm49IjouKiIscmVwbGFjZW1lbnQ9IiIpYC4gVG8gYWNjb3VudCBmb3IgbXVsdGlwbGUgdGVzdGluZywgYHIgbWNvbHMocmVzTGlzdFtbMV1dKSRkZXNjcmlwdGlvbls1XWAgYXJlIHJlcG9ydGVkLiBDb29rJ3MgY3V0b2ZmIHdhcyBzZXQgdG8gYHIgY29va3NgIGluIHRoaXMgYW5hbHlzaXMuIERpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyAoREVHcykgd2VyZSBmaWx0ZXJlZCB1c2luZyBhIGxpbmVhciBmb2xkIGNoYW5nZSBjdXRvZmYgb2YgYHIgbGluZWFyX2ZjX2ZpbHRlcmAgYW5kIGFkanVzdGVkIHAtdmFsdWUgb2YgYHIgYWxwaGFgLiAgCgojIFNlc3Npb24gSW5mbwoKIyMgRGF0ZSB0aGUgcmVwb3J0IHdhcyBnZW5lcmF0ZWQuCgpgYGB7ciByZXByb2R1Y2liaWxpdHkxLCBlY2hvPUZBTFNFfQojIyBEYXRlIHRoZSByZXBvcnQgd2FzIGdlbmVyYXRlZApTeXMudGltZSgpCmBgYAoKIyMgUGFyYW1ldGVycyAoZnJvbSB0aGUgbGlzdCBlbGVtZW50cyBmb3IgZWFjaCBwYXJhbXMke3doYXRldmVyfSkgdXNlZCB0byBnZW5lcmF0ZSB0aGlzIHJlcG9ydC4KCmBgYHtyIHBhcmFtc0xpc3QsIGVjaG89RkFMU0V9CmtuaXRyOjprYWJsZShhcy5kYXRhLmZyYW1lKHVubGlzdChwYXJhbXMpKSwgZm9ybWF0PSJtYXJrZG93biIpCmBgYAoKIyMgV2FsbGNsb2NrIHRpbWUgc3BlbnQgZ2VuZXJhdGluZyB0aGUgcmVwb3J0LgoKYGBge3IgcmVwcm9kdWNpYmlsaXR5MiwgZWNobz1GQUxTRX0KIyMgUHJvY2Vzc2luZyB0aW1lIGluIHNlY29uZHMKdG90YWxUaW1lIDwtIGRpZmYoYyhzdGFydFRpbWUsIFN5cy50aW1lKCkpKQpyb3VuZCh0b3RhbFRpbWUsIGRpZ2l0cz0zKQpgYGAKCiMjIGBSYCBzZXNzaW9uIGluZm9ybWF0aW9uLgoKYGBge3IgcmVwcm9kdWNpYmlsaXR5MywgZWNobz1GQUxTRX0KIyMgU2Vzc2lvbiBpbmZvCm9wdGlvbnMod2lkdGggPSAxMjApCnNlc3Npb25faW5mbygpCmBgYAoKIyMgUGFuZG9jIHZlcnNpb24gdXNlZDogYHIgcm1hcmtkb3duOjpwYW5kb2NfdmVyc2lvbigpYC4KCmBgYHtyICdzYXZlX2ZpbmFsJywgc2hvdz1GQUxTRX0KIyBTYXZlIGNvbXBsZXRlIHdvcmtzcGFjZQppZihpcy5uYShwYXJhbXMkZ3JvdXBfZmFjZXQpKSB7CiAgc2F2ZS5pbWFnZShmaWxlID0gZmlsZS5wYXRoKHBhdGhzJFJEYXRhLCAiQ29tcGxldGVfYW5hbHlzaXMuUkRhdGEiKSkKfSBlbHNlIHsKICBzYXZlLmltYWdlKGZpbGUgPSBmaWxlLnBhdGgocGF0aHMkUkRhdGEsIHBhc3RlMCgiQ29tcGxldGVfYW5hbHlzaXNfIiwgcGFyYW1zJGdyb3VwX2ZpbHRlciwgIi5SRGF0YSIpKSkKfQojIEFkZCBhIGZsYWcgdG8gdGhlIHN0YXJ0IG9mIHRoZSBzY3JpcHQgd2l0aCBpZiBzdGF0ZW1lbnRzIGluIGFsbCBjb2RlIGNodW5rcyB0byBjaGVjayBpZiB0aGUgZmxhZyBpcyBzZXQuCiMgSWYgZmxhZyBpcyBUUlVFLCBydW4gYW5hbHlzaXM7IGlmIGZsYWcgaXMgRkFMU0UsIFNLSVAgYW5hbHlzaXMgYW5kIGNvbnRpbnVlIGhlcmUgKGp1c3QgdG8gbW9kaWZ5IHBsb3R0aW5nIG91dHB1dCkuCmBgYAoKPGRpdiBjbGFzcz0idG9jaWZ5LWV4dGVuZC1wYWdlIiBkYXRhLXVuaXF1ZT0idG9jaWZ5LWV4dGVuZC1wYWdlIiBzdHlsZT0iaGVpZ2h0OiAwOyI+PC9kaXY+Cg==